public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/2] Add support for O32 FPXX ABI
@ 2014-04-04 14:26 Matthew Fortune
  2014-04-05  8:12 ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-04 14:26 UTC (permalink / raw)
  To: binutils; +Cc: Richard Sandiford

This patch implements the O32 FPXX ABI described in:

https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking

The proposed ABI was reviewed/discussed on the GCC mailing list starting
from the following posts:

http://gcc.gnu.org/ml/gcc/2014-02/msg00415.html
http://gcc.gnu.org/ml/gcc/2014-03/msg00191.html

It would be useful to confirm that the overall ABI is acceptable so that work
can start in other assemblers and linkers while any review comments are
addressed for binutils.

This work all involves GNU attributes in some way.  Main changes are:

* Introduction of new .module directive
* Implicit generation of GNU attributes based on command line options
* O32 FPXX ABI
** Introduction of new gnu_attributes
** Introduction of processor specific program header
** Macro expansion changes to ensure ABI compliance
* Named attribute support for .gnu_attribute

There is an open question about the use of program headers and exactly how
this would work. The reasons for opting to use one program header (without a
section) to specifically handle the O32 FPXX ABI are:

1) MIPS is running out of ELF flags.
2) ELF flags are not passed from kernel program loader to dynamic linker.
3) There are thousands of program header types available.
4) The indirection that would be required to obtain flag information if a
   section were also involved is more complex than necessary.
5) Program headers are only 32 bytes for 32-bit ELF and 64 bytes for 64-bit ELF
6) New features that require extra flags are not common and introducing further
   program headers should not incur excessive cost. (Though it would be nice to
   have more than 4 bits of processor specific flag data in a program header)
7) The (non)executable stack feature had similar requirements to the O32 FPXX
   ABI and eventually opted for a specific program header. This seems like good
   precedence albeit that PT_GNU_STACK is an architecture independent feature.

The patch makes no attempt to simplify the setup of correct FPU FR mode in bare
metal environments. This will be addressed separately.

Tested on mips-mti-linux-gnu and mips64-mti-linux-gnu with the second patch
which adds test cases.

Changes for GCC and glibc will be published shortly.

Regards,
Matthew

2014-04-04  Matthew Fortune  <matthew.fortune@imgtec.com>

include/

    * elf/mips.h (PT_MIPS_FPMODE, PF_MIPS_FPXX, PF_MIPS_FP64): 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.

bfd/

    * elfxx-mips.c (ABI_O32_P): New macro.
    (_bfd_mips_elf_additional_program_headers): Account for new FPMODE
    program header.
    (_bfd_mips_elf_modify_segment_map): Allocate or update FPMODE program
    header.
    (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_print_private_bfd_data): Print FPXX or FP64 based on
    PT_MIPS_FPMODE program header.

binutils/

    * readelf.c (get_mips_segment_type): Display name for PT_MIPS_FPMODE.
    (process_program_headers): Print FPXX or FP64 based on PT_MIPS_FPMODE
    program header.
    (display_mips_gnu_attribute): Handle Val_GNU_MIPS_ABI_FP_OLD_64 and
    Val_GNU_MIPS_ABI_FP_XX.

elfcpp/

    * elfcpp.h (PT_MIPS_FPMODE): 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): 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_*.
    (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.
    (md_obj_end): Use file_mips_check_options.
    (mips_parse_cpu): Update for globals.
    (mips_convert_symbolic_attribute): Support names for .gnu_attribute.

    * config/tc-mips.h (CONVERT_SYMBOLIC_ATTRIBUTE): Implement macro.

    * doc/c-mips.texi: Document -mfpxx.
---
 bfd/elfxx-mips.c     | 231 +++++++++++++++
 binutils/readelf.c   |  20 ++
 elfcpp/elfcpp.h      |   4 +-
 gas/config/tc-mips.c | 814 +++++++++++++++++++++++++++++++++------------------
 gas/config/tc-mips.h |   5 +
 gas/doc/c-mips.texi  |   8 +
 include/elf/mips.h   |  21 +-
 7 files changed, 810 insertions(+), 293 deletions(-)

diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index b44fc21..4e2035b 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -774,6 +774,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)
@@ -11756,6 +11760,7 @@ _bfd_mips_elf_additional_program_headers (bfd *abfd,
 					  struct bfd_link_info *info ATTRIBUTE_UNUSED)
 {
   asection *s;
+  obj_attribute *attr;
   int ret = 0;
 
   /* See if we need a PT_MIPS_REGINFO segment.  */
@@ -11781,6 +11786,15 @@ _bfd_mips_elf_additional_program_headers (bfd *abfd,
       && bfd_get_section_by_name (abfd, ".dynamic"))
     ++ret;
 
+  /* Allocate a PT_MIPS_FPMODE header to record a non-default ABI FR mode
+     requirement.  */
+  attr = elf_known_obj_attributes (abfd)[OBJ_ATTR_GNU];
+  if (ABI_O32_P (abfd)
+      && (attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_ANY
+	  || attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_XX
+	  || attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_64))
+    ++ret;
+
   return ret;
 }
 
@@ -11793,6 +11807,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
   asection *s;
   struct elf_segment_map *m, **pm;
   bfd_size_type amt;
+  obj_attribute *attr;
 
   /* If there is a .reginfo section, we need a PT_MIPS_REGINFO
      segment.  */
@@ -12023,6 +12038,41 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
 	}
     }
 
+  /* Search for the FPMODE program header.  */
+  for (pm = &elf_seg_map (abfd); *pm != NULL; pm = &(*pm)->next)
+    if ((*pm)->p_type == PT_MIPS_FPMODE)
+      break;
+
+  attr = elf_known_obj_attributes (abfd)[OBJ_ATTR_GNU];
+  if (ABI_O32_P (abfd)
+      && (attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_ANY
+	  || attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_XX
+	  || attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_64))
+    {
+      if (*pm == NULL)
+	{
+	  m = bfd_zalloc (abfd, sizeof (*m));
+	  if (m == NULL)
+	    return FALSE;
+
+	  m->p_type = PT_MIPS_FPMODE;
+
+	  /* We want to put it before PT_NULL segment.  */
+	  pm = &elf_seg_map (abfd);
+	  while (*pm != NULL && (*pm)->p_type != PT_NULL)
+	    pm = &(*pm)->next;
+
+	  m->next = *pm;
+	  *pm = m;
+	}
+      if (attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_64)
+	(*pm)->p_flags = PF_MIPS_FP64;
+      else
+	(*pm)->p_flags = PF_MIPS_FPXX;
+      (*pm)->p_flags_valid = 1;
+    }
+  /* Trust that a pre-existing PT_MIPS_FPMODE header remains valid.  */
+
   return TRUE;
 }
 

@@ -14370,6 +14420,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"),
@@ -14402,6 +14463,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"),
@@ -14424,6 +14499,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"),
@@ -14440,6 +14517,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)
 	      {
@@ -14463,6 +14633,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), "
@@ -14500,6 +14681,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 "
@@ -14798,6 +14996,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)
     {
@@ -14933,6 +15145,7 @@ bfd_boolean
 _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
 {
   FILE *file = ptr;
+  Elf_Internal_Phdr *p;
 
   BFD_ASSERT (abfd != NULL && ptr != NULL);
 
@@ -15015,6 +15228,24 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
   if (elf_elfheader (abfd)->e_flags & EF_MIPS_UCODE)
     fprintf (file, " [UCODE]");
 
+  p = elf_tdata (abfd)->phdr;
+  if (p != NULL)
+    {
+      unsigned int i;
+      unsigned int c = elf_elfheader (abfd)->e_phnum;
+
+      for (i = 0; i < c; i++, p++)
+	{
+	  if (p->p_type == PT_MIPS_FPMODE)
+	    {
+	      if (p->p_flags & PF_MIPS_FPXX)
+		fprintf (file, " [FPXX]");
+	      else if (p->p_flags & PF_MIPS_FP64)
+		fprintf (file, " [FP64]");
+	    }
+	}
+    }
+
   fputc ('\n', file);
 
   return TRUE;
diff --git a/binutils/readelf.c b/binutils/readelf.c
index c757a63..e074cc2 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -3163,6 +3163,8 @@ get_mips_segment_type (unsigned long type)
       return "RTPROC";
     case PT_MIPS_OPTIONS:
       return "OPTIONS";
+    case PT_MIPS_FPMODE:
+      return "FPMODE";
     default:
       break;
     }
@@ -4397,6 +4399,18 @@ process_program_headers (FILE * file)
 		    program_interpreter);
 	    }
 	  break;
+
+	case PT_MIPS_FPMODE:
+	  if (do_segments)
+	    {
+	      if (segment->p_flags & PF_MIPS_FPXX)
+		printf (_("\n      [O32 FPXX ABI]"));
+	      else if (segment->p_flags & PF_MIPS_FP64)
+		printf (_("\n      [O32 FP64 ABI]"));
+	      else
+		error (_("Unable to determine O32 FP ABI\n"));
+	    }
+	  break;
 	}
 
       if (do_segments)
@@ -12015,6 +12029,12 @@ display_mips_gnu_attribute (unsigned char * p,
 	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 (Any FPU)\n"));
+	  break;
 	case Val_GNU_MIPS_ABI_FP_64:
 	  printf (_("Hard float (MIPS32r2 64-bit FPU)\n"));
 	  break;
diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h
index 9511130..d8bbd2a 100644
--- a/elfcpp/elfcpp.h
+++ b/elfcpp/elfcpp.h
@@ -488,7 +488,9 @@ enum PT
   // Runtime procedure table.
   PT_MIPS_RTPROC = 0x70000001,
   // .MIPS.options section.
-  PT_MIPS_OPTIONS = 0x70000002
+  PT_MIPS_OPTIONS = 0x70000002,
+  // FP mode requirements
+  PT_MIPS_FPMODE = 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 318b0b5..9d313d5 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)
 
@@ -240,7 +242,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 +257,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 +316,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 +340,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
@@ -396,7 +410,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 +1283,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);
@@ -1376,6 +1391,7 @@ enum options
     OPTION_CONSTRUCT_FLOATS,
     OPTION_NO_CONSTRUCT_FLOATS,
     OPTION_FP64,
+    OPTION_FPXX,
     OPTION_GP64,
     OPTION_RELAX_BRANCH,
     OPTION_NO_RELAX_BRANCH,
@@ -1489,6 +1505,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},
@@ -1652,6 +1669,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.  */
@@ -1905,7 +1923,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;
@@ -1933,14 +1951,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;
 }
 
@@ -3336,7 +3355,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 ();
@@ -3594,6 +3613,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
@@ -3603,6 +3789,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;
@@ -4456,11 +4644,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)
     {
@@ -5314,13 +5513,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)
@@ -11512,14 +11710,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
@@ -13448,7 +13650,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;
       }
@@ -13498,39 +13700,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:
@@ -13574,32 +13776,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;
 
@@ -13668,11 +13870,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:
@@ -13684,11 +13886,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
@@ -13738,35 +13940,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:
@@ -13841,22 +14047,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)
@@ -13891,9 +14082,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)
@@ -13901,14 +14092,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)
@@ -13921,9 +14112,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);
 
@@ -13932,30 +14125,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
@@ -13963,67 +14143,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)
+  if (file_mips_opts.fp == 32)
     file_ase_explicit |= ASE_MIPS3D | 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 ();
 
@@ -14914,30 +15076,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;
 
@@ -14945,61 +15088,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)
@@ -15010,45 +15119,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
@@ -15061,7 +15154,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
@@ -15072,42 +15165,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;
@@ -15117,6 +15174,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;
@@ -15147,23 +15266,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 ();
 }
@@ -15210,6 +15393,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)
@@ -15287,6 +15472,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)
@@ -15390,6 +15577,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)
@@ -15418,6 +15607,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)
@@ -15465,6 +15656,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)
@@ -15699,6 +15892,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)
     {
@@ -17269,6 +17464,8 @@ mips_add_dot_label (symbolS *sym)
 void
 mips_elf_final_processing (void)
 {
+  int fpabi;
+
   /* Write out the register information.  */
   if (mips_abi != N64_ABI)
     {
@@ -17330,7 +17527,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;
@@ -17347,7 +17544,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;
 }
 

@@ -17462,6 +17661,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
@@ -18015,8 +18217,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
@@ -18259,3 +18461,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 184915e..c4f7ff8 100644
--- a/gas/doc/c-mips.texi
+++ b/gas/doc/c-mips.texi
@@ -119,6 +119,14 @@ 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}.
+
+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
diff --git a/include/elf/mips.h b/include/elf/mips.h
index 2949629..24e07ff 100644
--- a/include/elf/mips.h
+++ b/include/elf/mips.h
@@ -593,6 +593,19 @@ extern void bfd_mips_elf32_swap_reginfo_out
 
 /* .MIPS.options section.  */
 #define PT_MIPS_OPTIONS		0x70000002
+
+/* Records the FP mode requirement.  */
+#define PT_MIPS_FPMODE		0x70000003
+
+

+/* Processor specific program header flags.  */
+
+/* For PT_MIPS_FPMODE indicates that no mode is required.  */
+#define PF_MIPS_FPXX		0x10000000
+
+/* For PT_MIPS_FPMODE indicates that FP64 is required.  */
+#define PF_MIPS_FP64		0x20000000
+
 

 /* Processor specific dynamic array tags.  */
 
@@ -1157,7 +1170,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.  */
 
-- 
1.7.12.3

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-04 14:26 [PATCH 1/2] Add support for O32 FPXX ABI Matthew Fortune
@ 2014-04-05  8:12 ` Richard Sandiford
  2014-04-07  9:58   ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-05  8:12 UTC (permalink / raw)
  To: Matthew Fortune; +Cc: binutils

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> There is an open question about the use of program headers and exactly how
> this would work. The reasons for opting to use one program header (without a
> section) to specifically handle the O32 FPXX ABI are:

I still think this is a bad representation.

> 1) MIPS is running out of ELF flags.
> 2) ELF flags are not passed from kernel program loader to dynamic linker.
> 3) There are thousands of program header types available.
> 4) The indirection that would be required to obtain flag information if a
>    section were also involved is more complex than necessary.

As before, the indirection seems more complex when we just have one
piece of information we want to store.  But if we want to store other
things at a later stage, collecting it into a single structure could be
less complex then having to reassemble it from individual headers.

> 5) Program headers are only 32 bytes for 32-bit ELF and 64 bytes for 64-bit ELF
> 6) New features that require extra flags are not common and introducing further
>    program headers should not incur excessive cost. (Though it would be nice to
>    have more than 4 bits of processor specific flag data in a program header)

They don't seem that uncommon to me.  There are a few ASEs that don't
have ELF flags but probably should have.  I imagine they didn't get a
flag because there are so few left.  Also, we seem to be (ab?)using the
r2 ISA enumerations to mean r2-r5.  I imagine that if we had, say,
a 16-bit field to store the revision that we'd have used a straight
revision number instead.

So I certainly think we should design this on the assumption that we'll
need a few more pieces of information at some point.

I agree a 4-bit enumeration isn't good.  It certainly rules out being
able to use one program header like this to list all the ASEs in use.
We would presumably either have one header per ASE or group them arbitrarily
into groups of 4.

> 7) The (non)executable stack feature had similar requirements to the O32 FPXX
>    ABI and eventually opted for a specific program header. This seems like good
>    precedence albeit that PT_GNU_STACK is an architecture independent feature.

IMO PT_GNU_STACK is a “proper” segment.  It describes a region of memory
and is very similar to PT_GNU_RELRO.  The difference is of course that
PT_GNU_RELRO describes a region whose address and size are known at link
time while PT_GNU_STACK describes a region whose address and size are dynamic.

It looks to me like you're trying to use program headers like .dynamic tags
that apply to static executables as well as dynamic ones.  But to do that
you're having to shoehorn the payload into 4 bits of private flags,
which IMO just emphasises that this isn't the right representation.

This information is really like the load-time equivalent of .gnu_attributes.
.gnu_attributes collects the data into a single section and IMO the segment
side should too.

One representation would be to use the same data for the section and the
segment.  Or, if the attributes format seems too complicated for the loader,
we could add a simple pre-digested structure (with a version number as the
first field, say).

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-05  8:12 ` Richard Sandiford
@ 2014-04-07  9:58   ` Matthew Fortune
  2014-04-07 10:53     ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-07  9:58 UTC (permalink / raw)
  To: Richard Sandiford, macro, Joseph Myers (joseph@codesourcery.com); +Cc: binutils

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > There is an open question about the use of program headers and
> exactly
> > how this would work. The reasons for opting to use one program header
> > (without a
> > section) to specifically handle the O32 FPXX ABI are:
> 
> I still think this is a bad representation.
> 
> > 1) MIPS is running out of ELF flags.
> > 2) ELF flags are not passed from kernel program loader to dynamic
> linker.
> > 3) There are thousands of program header types available.
> > 4) The indirection that would be required to obtain flag information
> if a
> >    section were also involved is more complex than necessary.
> 
> As before, the indirection seems more complex when we just have one
> piece of information we want to store.  But if we want to store other
> things at a later stage, collecting it into a single structure could be
> less complex then having to reassemble it from individual headers.

Granted.
 
> > 5) Program headers are only 32 bytes for 32-bit ELF and 64 bytes for
> > 64-bit ELF
> > 6) New features that require extra flags are not common and
> introducing further
> >    program headers should not incur excessive cost. (Though it would
> be nice to
> >    have more than 4 bits of processor specific flag data in a program
> > header)
> 
> They don't seem that uncommon to me.  There are a few ASEs that don't
> have ELF flags but probably should have.  I imagine they didn't get a
> flag because there are so few left.  Also, we seem to be (ab?)using the
> r2 ISA enumerations to mean r2-r5.  I imagine that if we had, say, a
> 16-bit field to store the revision that we'd have used a straight
> revision number instead.

This is a bit subjective. We have an accumulation of features that lack
flags today but those features have appeared over several years at least.
Either way I do agree we shouldn't be forced to decide which features
deserve flags and which do not just because we have very few left.

> So I certainly think we should design this on the assumption that we'll
> need a few more pieces of information at some point.
> 
> I agree a 4-bit enumeration isn't good.  It certainly rules out being
> able to use one program header like this to list all the ASEs in use.
> We would presumably either have one header per ASE or group them
> arbitrarily into groups of 4.
> 
> > 7) The (non)executable stack feature had similar requirements to the
> O32 FPXX
> >    ABI and eventually opted for a specific program header. This seems
> like good
> >    precedence albeit that PT_GNU_STACK is an architecture independent
> feature.
> 
> IMO PT_GNU_STACK is a “proper” segment.  It describes a region of
> memory and is very similar to PT_GNU_RELRO.  The difference is of
> course that PT_GNU_RELRO describes a region whose address and size are
> known at link time while PT_GNU_STACK describes a region whose address
> and size are dynamic.
> 
> It looks to me like you're trying to use program headers like .dynamic
> tags that apply to static executables as well as dynamic ones.  But to
> do that you're having to shoehorn the payload into 4 bits of private
> flags, which IMO just emphasises that this isn't the right
> representation.

OK.

> This information is really like the load-time equivalent of
> .gnu_attributes.
> .gnu_attributes collects the data into a single section and IMO the
> segment side should too.
> 
> One representation would be to use the same data for the section and
> the segment.  Or, if the attributes format seems too complicated for
> the loader, we could add a simple pre-digested structure (with a
> version number as the first field, say).

Given that any new segment would need to be handled (in some ways) like
PT_NOTE then perhaps this does move the discussion towards PT_NOTE.
When I say it is like PT_NOTE I mean that the segment data would want 
to be stored as close to the program headers as possible to reduce the
amount of a file that needs to be read to check them. The cost of using
PT_NOTE would be the string comparison for the note name and the risk of
the notes being removed (I assume they can only be removed by specifically
doing so, at which point all bets are off anyway). I believe
notes have previously been rejected due to the string comparisons though:

http://sourceware.org/ml/binutils/2013-09/msg00099.html

Assuming we go for having a new program header and a segment to hold the
flags/structure... I could do with some pointers on how to go about
creating the segment, creating the header was relatively easy as I had
examples to go by but I can't see an example of creating a custom header
with a segment/data. I'm also unsure of how to get such data to be placed
close to the headers.

I've added Maciej and Joseph as I believe they have discussed this area
before.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-07  9:58   ` Matthew Fortune
@ 2014-04-07 10:53     ` Richard Sandiford
  2014-04-07 12:20       ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-07 10:53 UTC (permalink / raw)
  To: Matthew Fortune; +Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> Richard Sandiford <rdsandiford@googlemail.com> writes:
>> This information is really like the load-time equivalent of
>> .gnu_attributes.
>> .gnu_attributes collects the data into a single section and IMO the
>> segment side should too.
>> 
>> One representation would be to use the same data for the section and
>> the segment.  Or, if the attributes format seems too complicated for
>> the loader, we could add a simple pre-digested structure (with a
>> version number as the first field, say).
>
> Given that any new segment would need to be handled (in some ways) like
> PT_NOTE then perhaps this does move the discussion towards PT_NOTE.
> When I say it is like PT_NOTE I mean that the segment data would want 
> to be stored as close to the program headers as possible to reduce the
> amount of a file that needs to be read to check them. The cost of using
> PT_NOTE would be the string comparison for the note name and the risk of
> the notes being removed (I assume they can only be removed by specifically
> doing so, at which point all bets are off anyway). I believe
> notes have previously been rejected due to the string comparisons though:
>
> http://sourceware.org/ml/binutils/2013-09/msg00099.html
>
> Assuming we go for having a new program header and a segment to hold the
> flags/structure... I could do with some pointers on how to go about
> creating the segment, creating the header was relatively easy as I had
> examples to go by but I can't see an example of creating a custom header
> with a segment/data. I'm also unsure of how to get such data to be placed
> close to the headers.

What did you think about the idea of reusing the .gnu_attributes section
data as the segment data?  It seems simpler than the PT_NOTE format.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-07 10:53     ` Richard Sandiford
@ 2014-04-07 12:20       ` Matthew Fortune
  2014-04-07 13:21         ` Richard Sandiford
  2014-04-22 17:05         ` Joseph S. Myers
  0 siblings, 2 replies; 41+ messages in thread
From: Matthew Fortune @ 2014-04-07 12:20 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > Richard Sandiford <rdsandiford@googlemail.com> writes:
> >> This information is really like the load-time equivalent of
> >> .gnu_attributes.
> >> .gnu_attributes collects the data into a single section and IMO the
> >> segment side should too.
> >>
> >> One representation would be to use the same data for the section and
> >> the segment.  Or, if the attributes format seems too complicated for
> >> the loader, we could add a simple pre-digested structure (with a
> >> version number as the first field, say).
> >
> > Given that any new segment would need to be handled (in some ways)
> > like PT_NOTE then perhaps this does move the discussion towards
> PT_NOTE.
> > When I say it is like PT_NOTE I mean that the segment data would want
> > to be stored as close to the program headers as possible to reduce the
> > amount of a file that needs to be read to check them. The cost of
> > using PT_NOTE would be the string comparison for the note name and the
> > risk of the notes being removed (I assume they can only be removed by
> > specifically doing so, at which point all bets are off anyway). I
> > believe notes have previously been rejected due to the string
> comparisons though:
> >
> > http://sourceware.org/ml/binutils/2013-09/msg00099.html
> >
> > Assuming we go for having a new program header and a segment to hold
> > the flags/structure... I could do with some pointers on how to go
> > about creating the segment, creating the header was relatively easy as
> > I had examples to go by but I can't see an example of creating a
> > custom header with a segment/data. I'm also unsure of how to get such
> > data to be placed close to the headers.
> 
> What did you think about the idea of reusing the .gnu_attributes section
> data as the segment data?  It seems simpler than the PT_NOTE format.

I have no major objection. Slight concern over defining more of the MIPS
ABI around the 'gnu' vendor attributes which is a bit strange given the
MIPS architecture is not restricted to gnu tools. Having said that, MIPS is
already using these as critical parts of the ABI and following it through
to load time information as well then seems reasonable.

In order to use the pre-existing section for GNU attributes I guess it
would need to become SEC_ALLOC and get associated with the new program
header. Perhaps call it PT_MIPS_ATTRIBUTES or PT_GNU_ATTRIBUTES if this
were done for all architectures. And then somehow get that to be positioned
immediately after the notes segment.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-07 12:20       ` Matthew Fortune
@ 2014-04-07 13:21         ` Richard Sandiford
  2014-04-10  6:50           ` Matthew Fortune
  2014-04-22 17:05         ` Joseph S. Myers
  1 sibling, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-07 13:21 UTC (permalink / raw)
  To: Matthew Fortune; +Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> Richard Sandiford <rdsandiford@googlemail.com> writes:
>> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > Richard Sandiford <rdsandiford@googlemail.com> writes:
>> >> This information is really like the load-time equivalent of
>> >> .gnu_attributes.
>> >> .gnu_attributes collects the data into a single section and IMO the
>> >> segment side should too.
>> >>
>> >> One representation would be to use the same data for the section and
>> >> the segment.  Or, if the attributes format seems too complicated for
>> >> the loader, we could add a simple pre-digested structure (with a
>> >> version number as the first field, say).
>> >
>> > Given that any new segment would need to be handled (in some ways)
>> > like PT_NOTE then perhaps this does move the discussion towards
>> PT_NOTE.
>> > When I say it is like PT_NOTE I mean that the segment data would want
>> > to be stored as close to the program headers as possible to reduce the
>> > amount of a file that needs to be read to check them. The cost of
>> > using PT_NOTE would be the string comparison for the note name and the
>> > risk of the notes being removed (I assume they can only be removed by
>> > specifically doing so, at which point all bets are off anyway). I
>> > believe notes have previously been rejected due to the string
>> comparisons though:
>> >
>> > http://sourceware.org/ml/binutils/2013-09/msg00099.html
>> >
>> > Assuming we go for having a new program header and a segment to hold
>> > the flags/structure... I could do with some pointers on how to go
>> > about creating the segment, creating the header was relatively easy as
>> > I had examples to go by but I can't see an example of creating a
>> > custom header with a segment/data. I'm also unsure of how to get such
>> > data to be placed close to the headers.
>> 
>> What did you think about the idea of reusing the .gnu_attributes section
>> data as the segment data?  It seems simpler than the PT_NOTE format.
>
> I have no major objection. Slight concern over defining more of the MIPS
> ABI around the 'gnu' vendor attributes which is a bit strange given the
> MIPS architecture is not restricted to gnu tools. Having said that, MIPS is
> already using these as critical parts of the ABI and following it through
> to load time information as well then seems reasonable.
>
> In order to use the pre-existing section for GNU attributes I guess it
> would need to become SEC_ALLOC and get associated with the new program
> header.

Yeah, it should be SEC_ALLOC|SEC_LOAD for GNU/Linux, but I don't think
it should be for the baremetal *-elf targets (EMBEDDED=yes in the bfd
linker).  This would match the .reginfo behaviour.

> Perhaps call it PT_MIPS_ATTRIBUTES or PT_GNU_ATTRIBUTES if this were
> done for all architectures.

Yeah.  Might as well make it PT_GNU_ATTRIBUTES, even if it's something
that targets have to buy into.  Probably worth sending a separate RFC
to the list for that.

> And then somehow get that to be positioned immediately after the notes
> segment.

Yeah, at least in the case where it's loadable (and maybe otherwise too).
In the bfd linker this would be done by putting the .gnu_attributes in
the appropriate place in the linker script.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-07 13:21         ` Richard Sandiford
@ 2014-04-10  6:50           ` Matthew Fortune
  2014-04-10  8:31             ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-10  6:50 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
...
> > Perhaps call it PT_MIPS_ATTRIBUTES or PT_GNU_ATTRIBUTES if this were
> > done for all architectures.
> 
> Yeah.  Might as well make it PT_GNU_ATTRIBUTES, even if it's something
> that targets have to buy into.  Probably worth sending a separate RFC to
> the list for that.

I've had some discussion about this with Rich (now on cc). The current
thinking is that we don't want to have the MIPS ABI any more dependent on
the GNU attribute section but instead would prefer to migrate to a MIPS
attribute section. In order to achieve the migration then we would have to
do something like:

1) use the same attribute numbers for the FP abi and MSA abi as used in the
   GNU attributes section
2) At link time, if the FP or MSA attribute is set in the gnu attribute
   section then copy it to the MIPS attribute section (or if already set
   check they match)
3) In the output object only emit the MIPS attribute section (though I
   guess it would do no harm to emit both).
4) Perhaps emit both gnu and MIPS attributes in the assembler for
   backwards compatibility with older linkers. I think it would be
   sufficient to just do this when inferring an attribute from module options.
   The explicit .gnu_attribute and .mips_attribute would just set their
   Respective attributes but we would be phasing out the explicit use of such
   attributes anyway.

How does that sound? If it is OK would you be happy to review other aspects
of the patch under the assumption that the linker would use this newly
proposed program header scheme on the executables? It will take a few days
to rework this as I have to educate glibc about parsing attribute sections
and retest.

> > And then somehow get that to be positioned immediately after the notes
> > segment.
> 
> Yeah, at least in the case where it's loadable (and maybe otherwise
> too).
> In the bfd linker this would be done by putting the .gnu_attributes in
> the appropriate place in the linker script.

I realised this after sending my email, thanks.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-10  6:50           ` Matthew Fortune
@ 2014-04-10  8:31             ` Richard Sandiford
  2014-04-10 11:44               ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-10  8:31 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> Richard Sandiford <rdsandiford@googlemail.com> writes:
>> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> ...
>> > Perhaps call it PT_MIPS_ATTRIBUTES or PT_GNU_ATTRIBUTES if this were
>> > done for all architectures.
>> 
>> Yeah.  Might as well make it PT_GNU_ATTRIBUTES, even if it's something
>> that targets have to buy into.  Probably worth sending a separate RFC to
>> the list for that.
>
> I've had some discussion about this with Rich (now on cc). The current
> thinking is that we don't want to have the MIPS ABI any more dependent on
> the GNU attribute section but instead would prefer to migrate to a MIPS
> attribute section. In order to achieve the migration then we would have to
> do something like:
>
> 1) use the same attribute numbers for the FP abi and MSA abi as used in the
>    GNU attributes section
> 2) At link time, if the FP or MSA attribute is set in the gnu attribute
>    section then copy it to the MIPS attribute section (or if already set
>    check they match)
> 3) In the output object only emit the MIPS attribute section (though I
>    guess it would do no harm to emit both).
> 4) Perhaps emit both gnu and MIPS attributes in the assembler for
>    backwards compatibility with older linkers. I think it would be
>    sufficient to just do this when inferring an attribute from module options.
>    The explicit .gnu_attribute and .mips_attribute would just set their
>    Respective attributes but we would be phasing out the explicit use of such
>    attributes anyway.
>
> How does that sound? If it is OK would you be happy to review other aspects
> of the patch under the assumption that the linker would use this newly
> proposed program header scheme on the executables? It will take a few days
> to rework this as I have to educate glibc about parsing attribute sections
> and retest.

I understand why you'd prefer a MIPS gloss to the section names but it
seems a bit late to change it now.  Having the same information under
two different section names is just going to create a lot of unnecessary
complication.

Maybe the others on cc: feel more positively about it than me though.

Since the program header is new, calling it PT_MIPS_ATTRIBUTES would be
fine with me.  I slightly prefered PT_GNU_ATTRIBUTES for consistency
with the section name but at least there'd be no compatibility problem
with treating as a MIPS-specific header.  The meaning of the attributes
is target-dependent anyway.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-10  8:31             ` Richard Sandiford
@ 2014-04-10 11:44               ` Matthew Fortune
  2014-04-10 12:49                 ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-10 11:44 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > Richard Sandiford <rdsandiford@googlemail.com> writes:
> >> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > ...
> >> > Perhaps call it PT_MIPS_ATTRIBUTES or PT_GNU_ATTRIBUTES if this
> >> > were done for all architectures.
> >>
> >> Yeah.  Might as well make it PT_GNU_ATTRIBUTES, even if it's
> >> something that targets have to buy into.  Probably worth sending a
> >> separate RFC to the list for that.
> >
> > I've had some discussion about this with Rich (now on cc). The current
> > thinking is that we don't want to have the MIPS ABI any more dependent
> > on the GNU attribute section but instead would prefer to migrate to a
> > MIPS attribute section. In order to achieve the migration then we
> > would have to do something like:
> >
> > 1) use the same attribute numbers for the FP abi and MSA abi as used
> in the
> >    GNU attributes section
> > 2) At link time, if the FP or MSA attribute is set in the gnu
> attribute
> >    section then copy it to the MIPS attribute section (or if already
> set
> >    check they match)
> > 3) In the output object only emit the MIPS attribute section (though I
> >    guess it would do no harm to emit both).
> > 4) Perhaps emit both gnu and MIPS attributes in the assembler for
> >    backwards compatibility with older linkers. I think it would be
> >    sufficient to just do this when inferring an attribute from module
> options.
> >    The explicit .gnu_attribute and .mips_attribute would just set
> their
> >    Respective attributes but we would be phasing out the explicit use
> of such
> >    attributes anyway.
> >
> > How does that sound? If it is OK would you be happy to review other
> > aspects of the patch under the assumption that the linker would use
> > this newly proposed program header scheme on the executables? It will
> > take a few days to rework this as I have to educate glibc about
> > parsing attribute sections and retest.
> 
> I understand why you'd prefer a MIPS gloss to the section names but it
> seems a bit late to change it now.  Having the same information under
> two different section names is just going to create a lot of unnecessary
> complication.

Apart from just the general ABI concerns having to use the GNU name in 
LLVM tools is quite strange. Rich is particularly keen on having
this changed especially if we are going to make the section loadable.
Other aspects would be that non-gnu compilers may also introduce new
attributes which would have very little to do with 'gnu'.

> Maybe the others on cc: feel more positively about it than me though.
> 
> Since the program header is new, calling it PT_MIPS_ATTRIBUTES would be
> fine with me.  I slightly prefered PT_GNU_ATTRIBUTES for consistency
> with the section name but at least there'd be no compatibility problem
> with treating as a MIPS-specific header.  The meaning of the attributes
> is target-dependent anyway.

OK

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-10 11:44               ` Matthew Fortune
@ 2014-04-10 12:49                 ` Richard Sandiford
  2014-04-11  7:14                   ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-10 12:49 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> Richard Sandiford <rdsandiford@googlemail.com> writes:
>> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > Richard Sandiford <rdsandiford@googlemail.com> writes:
>> >> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > ...
>> >> > Perhaps call it PT_MIPS_ATTRIBUTES or PT_GNU_ATTRIBUTES if this
>> >> > were done for all architectures.
>> >>
>> >> Yeah.  Might as well make it PT_GNU_ATTRIBUTES, even if it's
>> >> something that targets have to buy into.  Probably worth sending a
>> >> separate RFC to the list for that.
>> >
>> > I've had some discussion about this with Rich (now on cc). The current
>> > thinking is that we don't want to have the MIPS ABI any more dependent
>> > on the GNU attribute section but instead would prefer to migrate to a
>> > MIPS attribute section. In order to achieve the migration then we
>> > would have to do something like:
>> >
>> > 1) use the same attribute numbers for the FP abi and MSA abi as used
>> in the
>> >    GNU attributes section
>> > 2) At link time, if the FP or MSA attribute is set in the gnu
>> attribute
>> >    section then copy it to the MIPS attribute section (or if already
>> set
>> >    check they match)
>> > 3) In the output object only emit the MIPS attribute section (though I
>> >    guess it would do no harm to emit both).
>> > 4) Perhaps emit both gnu and MIPS attributes in the assembler for
>> >    backwards compatibility with older linkers. I think it would be
>> >    sufficient to just do this when inferring an attribute from module
>> options.
>> >    The explicit .gnu_attribute and .mips_attribute would just set
>> their
>> >    Respective attributes but we would be phasing out the explicit use
>> of such
>> >    attributes anyway.
>> >
>> > How does that sound? If it is OK would you be happy to review other
>> > aspects of the patch under the assumption that the linker would use
>> > this newly proposed program header scheme on the executables? It will
>> > take a few days to rework this as I have to educate glibc about
>> > parsing attribute sections and retest.
>> 
>> I understand why you'd prefer a MIPS gloss to the section names but it
>> seems a bit late to change it now.  Having the same information under
>> two different section names is just going to create a lot of unnecessary
>> complication.
>
> Apart from just the general ABI concerns having to use the GNU name in 
> LLVM tools is quite strange. Rich is particularly keen on having
> this changed especially if we are going to make the section loadable.
> Other aspects would be that non-gnu compilers may also introduce new
> attributes which would have very little to do with 'gnu'.

The "gnu" doesn't mean that the tools that produced it were GNU tools.
It just means that the extension was originally defined as a GNU one,
regardless of how widely it ends up being used.  And I think "gnu" was
used not for marketing reasons but to avoid name clashes with anything
else that comes along.

Having LLVM support .gnu_attribute seems no stranger than having it
support the GNU DWARF extensions (which it does) or having lld generate
PT_GNU_STACK headers.

I just don't see any technical merits to this.  You didn't list it
explicitly, but I assume we'd have a .mips_attribute directive to go
alongside .gnu_attribute.  And like you say, .mips_attribute would need
to generate a .gnu_attributes section for compatiblity with existing
tools and .gnu_attribute would need to generate a .mips_attributes
section for compliance with the new ABI.  If any tool (including LLVM)
fails to emit .gnu_attributes, it's risking silent link incompatibilities
for the existing FP attribute.  So in the safe case, all we'd really end
up with is two sections with identical contents.  You could say that the
use of .gnu_attributes would be deprecated on MIPS, but from a QoI
perspective we'd want to continue to process old objects and (as far
as possible) continue to generate objects that are compatible with old
linkers.  Once we have the dual sections working, in practice the
.gnu_attributes side would never go away.

So I don't think you're going to convince me that this is a good idea.
But at the end of the day, it's a decision that's being made for branding
rather than technical reasons and you guys obviously control the ABI for
your architecture.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-10 12:49                 ` Richard Sandiford
@ 2014-04-11  7:14                   ` Matthew Fortune
  2014-04-11  8:26                     ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-11  7:14 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > Richard Sandiford <rdsandiford@googlemail.com> writes:
...
> >> I understand why you'd prefer a MIPS gloss to the section names but
> >> it seems a bit late to change it now.  Having the same information
> >> under two different section names is just going to create a lot of
> >> unnecessary complication.
> >
> > Apart from just the general ABI concerns having to use the GNU name in
> > LLVM tools is quite strange. Rich is particularly keen on having this
> > changed especially if we are going to make the section loadable.
> > Other aspects would be that non-gnu compilers may also introduce new
> > attributes which would have very little to do with 'gnu'.
> 
> The "gnu" doesn't mean that the tools that produced it were GNU tools.
> It just means that the extension was originally defined as a GNU one,
> regardless of how widely it ends up being used.  And I think "gnu" was
> used not for marketing reasons but to avoid name clashes with anything
> else that comes along.
> 
> Having LLVM support .gnu_attribute seems no stranger than having it
> support the GNU DWARF extensions (which it does) or having lld generate
> PT_GNU_STACK headers.

I mostly agree here with the one difference that those GNU features are
architecture independent.
 
> I just don't see any technical merits to this.  You didn't list it
> explicitly, but I assume we'd have a .mips_attribute directive to go
> alongside .gnu_attribute.  And like you say, .mips_attribute would need
> to generate a .gnu_attributes section for compatiblity with existing
> tools and .gnu_attribute would need to generate a .mips_attributes
> section for compliance with the new ABI.  If any tool (including LLVM)
> fails to emit .gnu_attributes, it's risking silent link
> incompatibilities for the existing FP attribute.  So in the safe case,
> all we'd really end up with is two sections with identical contents.
> You could say that the use of .gnu_attributes would be deprecated on
> MIPS, but from a QoI perspective we'd want to continue to process old
> objects and (as far as possible) continue to generate objects that are
> compatible with old linkers.  Once we have the dual sections working, in
> practice the .gnu_attributes side would never go away.

Indeed. Only the loadable section would have benefitted from the change.
 
> So I don't think you're going to convince me that this is a good idea.
> But at the end of the day, it's a decision that's being made for
> branding rather than technical reasons and you guys obviously control
> the ABI for your architecture.

I don't think anyone at Imagination will try to force an ABI decision
but as we look at formalising and recording the de-facto ABI used in the
GNU tools then there are likely to be a few things to talk through.

Independently of discussing MIPS vs GNU attributes I have been working
on implementing the parsing of attribute sections in glibc by stealing
code from BFD. I had not quite appreciated the complexity of the attribute
section and the fact it has strings and uleb128 encoded values means
everything is unaligned which is a bit frustrating. In retrospect I'm 
thinking the flexibility of the attribute section is not necessary for
what we need and the alternative proposal of a versioned structure seems
more desirable at this point. I'm wary of having to implement/maintain
this in several linkers and loaders (linux/*bsd etc) but the simpler
structure alternative would ease this.

Any objection to changing direction on the loadable section and
introducing a new section at link time to contain a simple structure?
Assuming that seems OK then I'd propose starting with:

struct mips_flags_0
{
uint32_t version;    /* mips_flags_0: version==0.  */
uint32_t e_flags2;
};

Any opinion on whether this should take a copy of the normal e_flags
field too, and/or whether the new flags field should follow the name
of the e_flags field or not? I can see pros and cons for including a
copy of the e_flags field. It may be better to uncompress the flags
so that there is a simple entry for architecture etc. This doesn't
really need dealing with right now though.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-11  7:14                   ` Matthew Fortune
@ 2014-04-11  8:26                     ` Richard Sandiford
  2014-04-11 13:14                       ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-11  8:26 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> So I don't think you're going to convince me that this is a good idea.
>> But at the end of the day, it's a decision that's being made for
>> branding rather than technical reasons and you guys obviously control
>> the ABI for your architecture.
>
> I don't think anyone at Imagination will try to force an ABI decision
> but as we look at formalising and recording the de-facto ABI used in the
> GNU tools then there are likely to be a few things to talk through.

Thanks.

> Independently of discussing MIPS vs GNU attributes I have been working
> on implementing the parsing of attribute sections in glibc by stealing
> code from BFD. I had not quite appreciated the complexity of the attribute
> section and the fact it has strings and uleb128 encoded values means
> everything is unaligned which is a bit frustrating. In retrospect I'm 
> thinking the flexibility of the attribute section is not necessary for
> what we need and the alternative proposal of a versioned structure seems
> more desirable at this point. I'm wary of having to implement/maintain
> this in several linkers and loaders (linux/*bsd etc) but the simpler
> structure alternative would ease this.

Sounds good.  I admit I hadn't looked into the format of the attributes
until now either. :-)

> Any objection to changing direction on the loadable section and
> introducing a new section at link time to contain a simple structure?
> Assuming that seems OK then I'd propose starting with:
>
> struct mips_flags_0
> {
> uint32_t version;    /* mips_flags_0: version==0.  */
> uint32_t e_flags2;
> };
>
> Any opinion on whether this should take a copy of the normal e_flags
> field too, and/or whether the new flags field should follow the name
> of the e_flags field or not? I can see pros and cons for including a
> copy of the e_flags field. It may be better to uncompress the flags
> so that there is a simple entry for architecture etc. This doesn't
> really need dealing with right now though.

I don't think we need to be too stingy with bytes, so personally I'd
like to go for the decompressed format.  The problem with flags is
that a lot of the things we want to model are enumerations, and if
you only define a 32-bit flags field, the tendancy is to allocate
enum fields that might end up being too small.

E.g., completely off the top of my head:

struct mips_flags_0
{
  uint32_t version;   /* mips_flags_0: version==0.  */
  uint8_t isa_level;  /* 1-5, 32, 64 */
  uint8_t isa_rev;    /* 0 for MIPS V and below, 1-5 so far otherwise */
  uint8_t gpr_size;   /* log2, 5, 6 or (possibly for r5900/r7900) 7 */
  uint8_t fpr_size;   /* log2, 5, 6 or (for MSA128) 7, 0 for FPxx */
  uint32_t isa_ext;   /* enumeration of processor-specific extensions */
  uint32_t ases;      /* bitmask of ASEs used */
  uint32_t flags1;    /* bitmask of general flags */
};

We could even throw in a second flags field to round it to a 64-bit boundary.

The flags should include the hard/soft-float distinction and (possibly)
single/double float.

It'd probably make sense for the ases field to include the ASEs that
already have ELF flags, so all the information is available in one place.
I don't mind either way about the other ELF flags.

Just a suggestion of course.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-11  8:26                     ` Richard Sandiford
@ 2014-04-11 13:14                       ` Matthew Fortune
  2014-04-11 16:29                         ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-11 13:14 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Richard Sandiford <rdsandiford@googlemail.com> writes:
> > Independently of discussing MIPS vs GNU attributes I have been working
> > on implementing the parsing of attribute sections in glibc by stealing
> > code from BFD. I had not quite appreciated the complexity of the
> > attribute section and the fact it has strings and uleb128 encoded
> > values means everything is unaligned which is a bit frustrating. In
> > retrospect I'm thinking the flexibility of the attribute section is
> > not necessary for what we need and the alternative proposal of a
> > versioned structure seems more desirable at this point. I'm wary of
> > having to implement/maintain this in several linkers and loaders
> > (linux/*bsd etc) but the simpler structure alternative would ease
> this.
> 
> Sounds good.  I admit I hadn't looked into the format of the attributes
> until now either. :-)
> 
> > Any objection to changing direction on the loadable section and
> > introducing a new section at link time to contain a simple structure?
> > Assuming that seems OK then I'd propose starting with:
> >
> > struct mips_flags_0
> > {
> > uint32_t version;    /* mips_flags_0: version==0.  */
> > uint32_t e_flags2;
> > };
> >
> > Any opinion on whether this should take a copy of the normal e_flags
> > field too, and/or whether the new flags field should follow the name
> > of the e_flags field or not? I can see pros and cons for including a
> > copy of the e_flags field. It may be better to uncompress the flags so
> > that there is a simple entry for architecture etc. This doesn't really
> > need dealing with right now though.
> 
> I don't think we need to be too stingy with bytes, so personally I'd
> like to go for the decompressed format.  The problem with flags is that
> a lot of the things we want to model are enumerations, and if you only
> define a 32-bit flags field, the tendancy is to allocate enum fields
> that might end up being too small.
> 
> E.g., completely off the top of my head:
> 
> struct mips_flags_0
> {
>   uint32_t version;   /* mips_flags_0: version==0.  */
>   uint8_t isa_level;  /* 1-5, 32, 64 */
>   uint8_t isa_rev;    /* 0 for MIPS V and below, 1-5 so far otherwise */
>   uint8_t gpr_size;   /* log2, 5, 6 or (possibly for r5900/r7900) 7 */
>   uint8_t fpr_size;   /* log2, 5, 6 or (for MSA128) 7, 0 for FPxx */
>   uint32_t isa_ext;   /* enumeration of processor-specific extensions */
>   uint32_t ases;      /* bitmask of ASEs used */
>   uint32_t flags1;    /* bitmask of general flags */
> };
> 
> We could even throw in a second flags field to round it to a 64-bit
> boundary.
> 
> The flags should include the hard/soft-float distinction and (possibly)
> single/double float.
> 
> It'd probably make sense for the ases field to include the ASEs that
> already have ELF flags, so all the information is available in one
> place.
> I don't mind either way about the other ELF flags.

No objections here. Perhaps including ABI would also be useful.

Since you mention the flagging of new ASEs I wonder if we can consider
that in more detail. When we need to flag something that is not already
included in the e_flags or attributes then I guess the assembler would
need to be emitting this structure too?

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-11 13:14                       ` Matthew Fortune
@ 2014-04-11 16:29                         ` Richard Sandiford
  2014-04-11 18:27                           ` Matthew Fortune
  2014-04-23 20:43                           ` Joseph S. Myers
  0 siblings, 2 replies; 41+ messages in thread
From: Richard Sandiford @ 2014-04-11 16:29 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> Richard Sandiford <rdsandiford@googlemail.com> writes:
>> > Independently of discussing MIPS vs GNU attributes I have been working
>> > on implementing the parsing of attribute sections in glibc by stealing
>> > code from BFD. I had not quite appreciated the complexity of the
>> > attribute section and the fact it has strings and uleb128 encoded
>> > values means everything is unaligned which is a bit frustrating. In
>> > retrospect I'm thinking the flexibility of the attribute section is
>> > not necessary for what we need and the alternative proposal of a
>> > versioned structure seems more desirable at this point. I'm wary of
>> > having to implement/maintain this in several linkers and loaders
>> > (linux/*bsd etc) but the simpler structure alternative would ease
>> this.
>> 
>> Sounds good.  I admit I hadn't looked into the format of the attributes
>> until now either. :-)
>> 
>> > Any objection to changing direction on the loadable section and
>> > introducing a new section at link time to contain a simple structure?
>> > Assuming that seems OK then I'd propose starting with:
>> >
>> > struct mips_flags_0
>> > {
>> > uint32_t version;    /* mips_flags_0: version==0.  */
>> > uint32_t e_flags2;
>> > };
>> >
>> > Any opinion on whether this should take a copy of the normal e_flags
>> > field too, and/or whether the new flags field should follow the name
>> > of the e_flags field or not? I can see pros and cons for including a
>> > copy of the e_flags field. It may be better to uncompress the flags so
>> > that there is a simple entry for architecture etc. This doesn't really
>> > need dealing with right now though.
>> 
>> I don't think we need to be too stingy with bytes, so personally I'd
>> like to go for the decompressed format.  The problem with flags is that
>> a lot of the things we want to model are enumerations, and if you only
>> define a 32-bit flags field, the tendancy is to allocate enum fields
>> that might end up being too small.
>> 
>> E.g., completely off the top of my head:
>> 
>> struct mips_flags_0
>> {
>>   uint32_t version;   /* mips_flags_0: version==0.  */
>>   uint8_t isa_level;  /* 1-5, 32, 64 */
>>   uint8_t isa_rev;    /* 0 for MIPS V and below, 1-5 so far otherwise */
>>   uint8_t gpr_size;   /* log2, 5, 6 or (possibly for r5900/r7900) 7 */
>>   uint8_t fpr_size;   /* log2, 5, 6 or (for MSA128) 7, 0 for FPxx */
>>   uint32_t isa_ext;   /* enumeration of processor-specific extensions */
>>   uint32_t ases;      /* bitmask of ASEs used */
>>   uint32_t flags1;    /* bitmask of general flags */
>> };
>> 
>> We could even throw in a second flags field to round it to a 64-bit
>> boundary.
>> 
>> The flags should include the hard/soft-float distinction and (possibly)
>> single/double float.
>> 
>> It'd probably make sense for the ases field to include the ASEs that
>> already have ELF flags, so all the information is available in one
>> place.
>> I don't mind either way about the other ELF flags.
>
> No objections here. Perhaps including ABI would also be useful.

Don't mind either way on that one.  I hope we're not likely to add
yet more ABIs (surely 6 is enough) and the distinction between o32,
n32 and n64 really has to be made in the ELF header anyway.
But I don't see it doing any harm.

Perhaps also an ABI revision number, so that we can bump it when
adding things that old loaders won't be able to handle correctly.
E.g. the suggestion for ifuncs was that we'd add a new dynamic tag
for the start of the implicitly-relocated local GOT.

When setting the revision to something higher than what we have now,
we could use a dummy relocation to choke old loaders.

> Since you mention the flagging of new ASEs I wonder if we can consider
> that in more detail. When we need to flag something that is not already
> included in the e_flags or attributes then I guess the assembler would
> need to be emitting this structure too?

Probably once we define the structure we'll want to emit it unconditionally.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-11 16:29                         ` Richard Sandiford
@ 2014-04-11 18:27                           ` Matthew Fortune
  2014-04-11 18:31                             ` Richard Sandiford
  2014-04-23 20:43                           ` Joseph S. Myers
  1 sibling, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-11 18:27 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > Richard Sandiford <rdsandiford@googlemail.com> writes:
> >> > Independently of discussing MIPS vs GNU attributes I have been
> >> > working on implementing the parsing of attribute sections in glibc
> >> > by stealing code from BFD. I had not quite appreciated the
> >> > complexity of the attribute section and the fact it has strings and
> >> > uleb128 encoded values means everything is unaligned which is a bit
> >> > frustrating. In retrospect I'm thinking the flexibility of the
> >> > attribute section is not necessary for what we need and the
> >> > alternative proposal of a versioned structure seems more desirable
> >> > at this point. I'm wary of having to implement/maintain this in
> >> > several linkers and loaders (linux/*bsd etc) but the simpler
> >> > structure alternative would ease
> >> this.
> >>
> >> Sounds good.  I admit I hadn't looked into the format of the
> >> attributes until now either. :-)
> >>
> >> > Any objection to changing direction on the loadable section and
> >> > introducing a new section at link time to contain a simple
> structure?
> >> > Assuming that seems OK then I'd propose starting with:
> >> >
> >> > struct mips_flags_0
> >> > {
> >> > uint32_t version;    /* mips_flags_0: version==0.  */
> >> > uint32_t e_flags2;
> >> > };
> >> >
> >> > Any opinion on whether this should take a copy of the normal
> >> > e_flags field too, and/or whether the new flags field should follow
> >> > the name of the e_flags field or not? I can see pros and cons for
> >> > including a copy of the e_flags field. It may be better to
> >> > uncompress the flags so that there is a simple entry for
> >> > architecture etc. This doesn't really need dealing with right now
> though.
> >>
> >> I don't think we need to be too stingy with bytes, so personally I'd
> >> like to go for the decompressed format.  The problem with flags is
> >> that a lot of the things we want to model are enumerations, and if
> >> you only define a 32-bit flags field, the tendancy is to allocate
> >> enum fields that might end up being too small.
> >>
> >> E.g., completely off the top of my head:
> >>
> >> struct mips_flags_0
> >> {
> >>   uint32_t version;   /* mips_flags_0: version==0.  */
> >>   uint8_t isa_level;  /* 1-5, 32, 64 */
> >>   uint8_t isa_rev;    /* 0 for MIPS V and below, 1-5 so far otherwise
> */
> >>   uint8_t gpr_size;   /* log2, 5, 6 or (possibly for r5900/r7900) 7
> */
> >>   uint8_t fpr_size;   /* log2, 5, 6 or (for MSA128) 7, 0 for FPxx */
> >>   uint32_t isa_ext;   /* enumeration of processor-specific extensions
> */
> >>   uint32_t ases;      /* bitmask of ASEs used */
> >>   uint32_t flags1;    /* bitmask of general flags */
> >> };
> >>
> >> We could even throw in a second flags field to round it to a 64-bit
> >> boundary.
> >>
> >> The flags should include the hard/soft-float distinction and
> >> (possibly) single/double float.
> >>
> >> It'd probably make sense for the ases field to include the ASEs that
> >> already have ELF flags, so all the information is available in one
> >> place.
> >> I don't mind either way about the other ELF flags.
> >
> > No objections here. Perhaps including ABI would also be useful.
> 
> Don't mind either way on that one.  I hope we're not likely to add yet
> more ABIs (surely 6 is enough) and the distinction between o32,
> n32 and n64 really has to be made in the ELF header anyway.
> But I don't see it doing any harm.
> 
> Perhaps also an ABI revision number, so that we can bump it when adding
> things that old loaders won't be able to handle correctly.
> E.g. the suggestion for ifuncs was that we'd add a new dynamic tag for
> the start of the implicitly-relocated local GOT.
> 
> When setting the revision to something higher than what we have now, we
> could use a dummy relocation to choke old loaders.
> 
> > Since you mention the flagging of new ASEs I wonder if we can consider
> > that in more detail. When we need to flag something that is not
> > already included in the e_flags or attributes then I guess the
> > assembler would need to be emitting this structure too?
> 
> Probably once we define the structure we'll want to emit it
> unconditionally.

OK. I expect the linker would then want to check e_flags and gnu attributes agree with the content of the structure (if present). It may then be easiest to move all the e_flag / attribute checks to be performed using the structure and have the linker infer the structure content when it is not present in an input object. Any thoughts on that side of things?

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-11 18:27                           ` Matthew Fortune
@ 2014-04-11 18:31                             ` Richard Sandiford
  2014-04-15 12:28                               ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-11 18:31 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> Richard Sandiford <rdsandiford@googlemail.com> writes:
>> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > Richard Sandiford <rdsandiford@googlemail.com> writes:
>> >> > Independently of discussing MIPS vs GNU attributes I have been
>> >> > working on implementing the parsing of attribute sections in glibc
>> >> > by stealing code from BFD. I had not quite appreciated the
>> >> > complexity of the attribute section and the fact it has strings and
>> >> > uleb128 encoded values means everything is unaligned which is a bit
>> >> > frustrating. In retrospect I'm thinking the flexibility of the
>> >> > attribute section is not necessary for what we need and the
>> >> > alternative proposal of a versioned structure seems more desirable
>> >> > at this point. I'm wary of having to implement/maintain this in
>> >> > several linkers and loaders (linux/*bsd etc) but the simpler
>> >> > structure alternative would ease
>> >> this.
>> >>
>> >> Sounds good.  I admit I hadn't looked into the format of the
>> >> attributes until now either. :-)
>> >>
>> >> > Any objection to changing direction on the loadable section and
>> >> > introducing a new section at link time to contain a simple
>> structure?
>> >> > Assuming that seems OK then I'd propose starting with:
>> >> >
>> >> > struct mips_flags_0
>> >> > {
>> >> > uint32_t version;    /* mips_flags_0: version==0.  */
>> >> > uint32_t e_flags2;
>> >> > };
>> >> >
>> >> > Any opinion on whether this should take a copy of the normal
>> >> > e_flags field too, and/or whether the new flags field should follow
>> >> > the name of the e_flags field or not? I can see pros and cons for
>> >> > including a copy of the e_flags field. It may be better to
>> >> > uncompress the flags so that there is a simple entry for
>> >> > architecture etc. This doesn't really need dealing with right now
>> though.
>> >>
>> >> I don't think we need to be too stingy with bytes, so personally I'd
>> >> like to go for the decompressed format.  The problem with flags is
>> >> that a lot of the things we want to model are enumerations, and if
>> >> you only define a 32-bit flags field, the tendancy is to allocate
>> >> enum fields that might end up being too small.
>> >>
>> >> E.g., completely off the top of my head:
>> >>
>> >> struct mips_flags_0
>> >> {
>> >>   uint32_t version;   /* mips_flags_0: version==0.  */
>> >>   uint8_t isa_level;  /* 1-5, 32, 64 */
>> >>   uint8_t isa_rev;    /* 0 for MIPS V and below, 1-5 so far otherwise
>> */
>> >>   uint8_t gpr_size;   /* log2, 5, 6 or (possibly for r5900/r7900) 7
>> */
>> >>   uint8_t fpr_size;   /* log2, 5, 6 or (for MSA128) 7, 0 for FPxx */
>> >>   uint32_t isa_ext;   /* enumeration of processor-specific extensions
>> */
>> >>   uint32_t ases;      /* bitmask of ASEs used */
>> >>   uint32_t flags1;    /* bitmask of general flags */
>> >> };
>> >>
>> >> We could even throw in a second flags field to round it to a 64-bit
>> >> boundary.
>> >>
>> >> The flags should include the hard/soft-float distinction and
>> >> (possibly) single/double float.
>> >>
>> >> It'd probably make sense for the ases field to include the ASEs that
>> >> already have ELF flags, so all the information is available in one
>> >> place.
>> >> I don't mind either way about the other ELF flags.
>> >
>> > No objections here. Perhaps including ABI would also be useful.
>> 
>> Don't mind either way on that one.  I hope we're not likely to add yet
>> more ABIs (surely 6 is enough) and the distinction between o32,
>> n32 and n64 really has to be made in the ELF header anyway.
>> But I don't see it doing any harm.
>> 
>> Perhaps also an ABI revision number, so that we can bump it when adding
>> things that old loaders won't be able to handle correctly.
>> E.g. the suggestion for ifuncs was that we'd add a new dynamic tag for
>> the start of the implicitly-relocated local GOT.
>> 
>> When setting the revision to something higher than what we have now, we
>> could use a dummy relocation to choke old loaders.
>> 
>> > Since you mention the flagging of new ASEs I wonder if we can consider
>> > that in more detail. When we need to flag something that is not
>> > already included in the e_flags or attributes then I guess the
>> > assembler would need to be emitting this structure too?
>> 
>> Probably once we define the structure we'll want to emit it
>> unconditionally.
>
> OK. I expect the linker would then want to check e_flags and gnu
> attributes agree with the content of the structure (if present). It may
> then be easiest to move all the e_flag / attribute checks to be
> performed using the structure and have the linker infer the structure
> content when it is not present in an input object. Any thoughts on that
> side of things?

Yeah, I was thinking the same.  We'd use the structure for all internal
processing and convert in and out as necessary.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-11 18:31                             ` Richard Sandiford
@ 2014-04-15 12:28                               ` Matthew Fortune
  2014-04-15 20:21                                 ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-15 12:28 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > Richard Sandiford <rdsandiford@googlemail.com> writes:
> >> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> >> > Richard Sandiford <rdsandiford@googlemail.com> writes:
> >> >> > Independently of discussing MIPS vs GNU attributes I have been
> >> >> > working on implementing the parsing of attribute sections in
> >> >> > glibc by stealing code from BFD. I had not quite appreciated the
> >> >> > complexity of the attribute section and the fact it has strings
> >> >> > and
> >> >> > uleb128 encoded values means everything is unaligned which is a
> >> >> > bit frustrating. In retrospect I'm thinking the flexibility of
> >> >> > the attribute section is not necessary for what we need and the
> >> >> > alternative proposal of a versioned structure seems more
> >> >> > desirable at this point. I'm wary of having to
> >> >> > implement/maintain this in several linkers and loaders
> >> >> > (linux/*bsd etc) but the simpler structure alternative would
> >> >> > ease
> >> >> this.
> >> >>
> >> >> Sounds good.  I admit I hadn't looked into the format of the
> >> >> attributes until now either. :-)
> >> >>
> >> >> > Any objection to changing direction on the loadable section and
> >> >> > introducing a new section at link time to contain a simple
> >> structure?
> >> >> > Assuming that seems OK then I'd propose starting with:
> >> >> >
> >> >> > struct mips_flags_0
> >> >> > {
> >> >> > uint32_t version;    /* mips_flags_0: version==0.  */
> >> >> > uint32_t e_flags2;
> >> >> > };
> >> >> >
> >> >> > Any opinion on whether this should take a copy of the normal
> >> >> > e_flags field too, and/or whether the new flags field should
> >> >> > follow the name of the e_flags field or not? I can see pros and
> >> >> > cons for including a copy of the e_flags field. It may be better
> >> >> > to uncompress the flags so that there is a simple entry for
> >> >> > architecture etc. This doesn't really need dealing with right
> >> >> > now
> >> though.
> >> >>
> >> >> I don't think we need to be too stingy with bytes, so personally
> >> >> I'd like to go for the decompressed format.  The problem with
> >> >> flags is that a lot of the things we want to model are
> >> >> enumerations, and if you only define a 32-bit flags field, the
> >> >> tendancy is to allocate enum fields that might end up being too
> small.
> >> >>
> >> >> E.g., completely off the top of my head:
> >> >>
> >> >> struct mips_flags_0
> >> >> {
> >> >>   uint32_t version;   /* mips_flags_0: version==0.  */
> >> >>   uint8_t isa_level;  /* 1-5, 32, 64 */
> >> >>   uint8_t isa_rev;    /* 0 for MIPS V and below, 1-5 so far
> otherwise
> >> */
> >> >>   uint8_t gpr_size;   /* log2, 5, 6 or (possibly for r5900/r7900)
> 7
> >> */
> >> >>   uint8_t fpr_size;   /* log2, 5, 6 or (for MSA128) 7, 0 for FPxx
> */
> >> >>   uint32_t isa_ext;   /* enumeration of processor-specific
> extensions
> >> */
> >> >>   uint32_t ases;      /* bitmask of ASEs used */
> >> >>   uint32_t flags1;    /* bitmask of general flags */
> >> >> };
> >> >>
> >> >> We could even throw in a second flags field to round it to a
> >> >> 64-bit boundary.
> >> >>
> >> >> The flags should include the hard/soft-float distinction and
> >> >> (possibly) single/double float.
> >> >>
> >> >> It'd probably make sense for the ases field to include the ASEs
> >> >> that already have ELF flags, so all the information is available
> >> >> in one place.
> >> >> I don't mind either way about the other ELF flags.
> >> >
> >> > No objections here. Perhaps including ABI would also be useful.
> >>
> >> Don't mind either way on that one.  I hope we're not likely to add
> >> yet more ABIs (surely 6 is enough) and the distinction between o32,
> >> n32 and n64 really has to be made in the ELF header anyway.
> >> But I don't see it doing any harm.
> >>
> >> Perhaps also an ABI revision number, so that we can bump it when
> >> adding things that old loaders won't be able to handle correctly.
> >> E.g. the suggestion for ifuncs was that we'd add a new dynamic tag
> >> for the start of the implicitly-relocated local GOT.
> >>
> >> When setting the revision to something higher than what we have now,
> >> we could use a dummy relocation to choke old loaders.
> >>
> >> > Since you mention the flagging of new ASEs I wonder if we can
> >> > consider that in more detail. When we need to flag something that
> >> > is not already included in the e_flags or attributes then I guess
> >> > the assembler would need to be emitting this structure too?
> >>
> >> Probably once we define the structure we'll want to emit it
> >> unconditionally.
> >
> > OK. I expect the linker would then want to check e_flags and gnu
> > attributes agree with the content of the structure (if present). It
> > may then be easiest to move all the e_flag / attribute checks to be
> > performed using the structure and have the linker infer the structure
> > content when it is not present in an input object. Any thoughts on
> > that side of things?
> 
> Yeah, I was thinking the same.  We'd use the structure for all internal
> processing and convert in and out as necessary.

I've been reviewing the .MIPS.options section to see whether it would be
a suitable place for storing this new information and from what I can tell
it would fit quite nicely. The .MIPS.options section is only emitted for
N64 currently but I don't see any technical reason why it cannot be
emitted for 32-bit or 64-bit ELFs so I would propose using it for all 3
of the most used ABIs (O32, N32, N64). Perhaps going so far as to stop
emitting the .reginfo section for O32 and N32 as that information fits
into the .MIPS.options section anyway. The structure of this section is
quite neat in that the option descriptor header states the total size
of the header+payload and therefore can be easily skipped. The maximum
payload per descriptor is 255 bytes including the 8 byte header but that
does not seem restrictive. I would add a new descriptor type and be able
to re-use a reasonable amount of existing code. There is a program header
and dynamic tag already defined for this section which are only emitted
for irix currently but that is easily resolved.

Any objections to this?

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-15 12:28                               ` Matthew Fortune
@ 2014-04-15 20:21                                 ` Richard Sandiford
  2014-04-15 21:09                                   ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-15 20:21 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> Richard Sandiford <rdsandiford@googlemail.com> writes:
>> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > Richard Sandiford <rdsandiford@googlemail.com> writes:
>> >> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> >> > Richard Sandiford <rdsandiford@googlemail.com> writes:
>> >> >> > Independently of discussing MIPS vs GNU attributes I have been
>> >> >> > working on implementing the parsing of attribute sections in
>> >> >> > glibc by stealing code from BFD. I had not quite appreciated the
>> >> >> > complexity of the attribute section and the fact it has strings
>> >> >> > and
>> >> >> > uleb128 encoded values means everything is unaligned which is a
>> >> >> > bit frustrating. In retrospect I'm thinking the flexibility of
>> >> >> > the attribute section is not necessary for what we need and the
>> >> >> > alternative proposal of a versioned structure seems more
>> >> >> > desirable at this point. I'm wary of having to
>> >> >> > implement/maintain this in several linkers and loaders
>> >> >> > (linux/*bsd etc) but the simpler structure alternative would
>> >> >> > ease
>> >> >> this.
>> >> >>
>> >> >> Sounds good.  I admit I hadn't looked into the format of the
>> >> >> attributes until now either. :-)
>> >> >>
>> >> >> > Any objection to changing direction on the loadable section and
>> >> >> > introducing a new section at link time to contain a simple
>> >> structure?
>> >> >> > Assuming that seems OK then I'd propose starting with:
>> >> >> >
>> >> >> > struct mips_flags_0
>> >> >> > {
>> >> >> > uint32_t version;    /* mips_flags_0: version==0.  */
>> >> >> > uint32_t e_flags2;
>> >> >> > };
>> >> >> >
>> >> >> > Any opinion on whether this should take a copy of the normal
>> >> >> > e_flags field too, and/or whether the new flags field should
>> >> >> > follow the name of the e_flags field or not? I can see pros and
>> >> >> > cons for including a copy of the e_flags field. It may be better
>> >> >> > to uncompress the flags so that there is a simple entry for
>> >> >> > architecture etc. This doesn't really need dealing with right
>> >> >> > now
>> >> though.
>> >> >>
>> >> >> I don't think we need to be too stingy with bytes, so personally
>> >> >> I'd like to go for the decompressed format.  The problem with
>> >> >> flags is that a lot of the things we want to model are
>> >> >> enumerations, and if you only define a 32-bit flags field, the
>> >> >> tendancy is to allocate enum fields that might end up being too
>> small.
>> >> >>
>> >> >> E.g., completely off the top of my head:
>> >> >>
>> >> >> struct mips_flags_0
>> >> >> {
>> >> >>   uint32_t version;   /* mips_flags_0: version==0.  */
>> >> >>   uint8_t isa_level;  /* 1-5, 32, 64 */
>> >> >>   uint8_t isa_rev;    /* 0 for MIPS V and below, 1-5 so far
>> otherwise
>> >> */
>> >> >>   uint8_t gpr_size;   /* log2, 5, 6 or (possibly for r5900/r7900)
>> 7
>> >> */
>> >> >>   uint8_t fpr_size;   /* log2, 5, 6 or (for MSA128) 7, 0 for FPxx
>> */
>> >> >>   uint32_t isa_ext;   /* enumeration of processor-specific
>> extensions
>> >> */
>> >> >>   uint32_t ases;      /* bitmask of ASEs used */
>> >> >>   uint32_t flags1;    /* bitmask of general flags */
>> >> >> };
>> >> >>
>> >> >> We could even throw in a second flags field to round it to a
>> >> >> 64-bit boundary.
>> >> >>
>> >> >> The flags should include the hard/soft-float distinction and
>> >> >> (possibly) single/double float.
>> >> >>
>> >> >> It'd probably make sense for the ases field to include the ASEs
>> >> >> that already have ELF flags, so all the information is available
>> >> >> in one place.
>> >> >> I don't mind either way about the other ELF flags.
>> >> >
>> >> > No objections here. Perhaps including ABI would also be useful.
>> >>
>> >> Don't mind either way on that one.  I hope we're not likely to add
>> >> yet more ABIs (surely 6 is enough) and the distinction between o32,
>> >> n32 and n64 really has to be made in the ELF header anyway.
>> >> But I don't see it doing any harm.
>> >>
>> >> Perhaps also an ABI revision number, so that we can bump it when
>> >> adding things that old loaders won't be able to handle correctly.
>> >> E.g. the suggestion for ifuncs was that we'd add a new dynamic tag
>> >> for the start of the implicitly-relocated local GOT.
>> >>
>> >> When setting the revision to something higher than what we have now,
>> >> we could use a dummy relocation to choke old loaders.
>> >>
>> >> > Since you mention the flagging of new ASEs I wonder if we can
>> >> > consider that in more detail. When we need to flag something that
>> >> > is not already included in the e_flags or attributes then I guess
>> >> > the assembler would need to be emitting this structure too?
>> >>
>> >> Probably once we define the structure we'll want to emit it
>> >> unconditionally.
>> >
>> > OK. I expect the linker would then want to check e_flags and gnu
>> > attributes agree with the content of the structure (if present). It
>> > may then be easiest to move all the e_flag / attribute checks to be
>> > performed using the structure and have the linker infer the structure
>> > content when it is not present in an input object. Any thoughts on
>> > that side of things?
>> 
>> Yeah, I was thinking the same.  We'd use the structure for all internal
>> processing and convert in and out as necessary.
>
> I've been reviewing the .MIPS.options section to see whether it would be
> a suitable place for storing this new information and from what I can tell
> it would fit quite nicely. The .MIPS.options section is only emitted for
> N64 currently but I don't see any technical reason why it cannot be
> emitted for 32-bit or 64-bit ELFs so I would propose using it for all 3
> of the most used ABIs (O32, N32, N64). Perhaps going so far as to stop
> emitting the .reginfo section for O32 and N32 as that information fits
> into the .MIPS.options section anyway. The structure of this section is
> quite neat in that the option descriptor header states the total size
> of the header+payload and therefore can be easily skipped. The maximum
> payload per descriptor is 255 bytes including the 8 byte header but that
> does not seem restrictive. I would add a new descriptor type and be able
> to re-use a reasonable amount of existing code. There is a program header
> and dynamic tag already defined for this section which are only emitted
> for irix currently but that is easily resolved.

How much code would it save though?  It means you don't have to define
a new header, but that's only a small saving.

I agree it'd be possible to use .MIPS.options, but in some ways it seems
a bit heavyweight for what we want.  Like with PT_NOTE, looking up
the data would involve a search for the right kind.  In this case
we'd be searching for an integer rather than a string, so it's much
less of a problem, but it still seems like an unnecessary overhead.
FWIW, looking at an n64 libc.so I have lying around, the .MIPS.options
is 0xe948 bytes in size.

I suppose you could add a rule that the new kind needs to come first,
but that doesn't seem in the spirit of the thing.  If we're adding
special rules like that then we might as well add a new header instead.

Also, like you say, it would involve either a change to the ELF32 ABIs
or having duplicated information there.  We'd probably have to go for
the duplicated information since .reginfo is part of the psABI.

I'm not totally opposed or anything.  I'm just not sure it buys much.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-15 20:21                                 ` Richard Sandiford
@ 2014-04-15 21:09                                   ` Matthew Fortune
  2014-04-16  8:25                                     ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-15 21:09 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

> > I've been reviewing the .MIPS.options section to see whether it would
> > be a suitable place for storing this new information and from what I
> > can tell it would fit quite nicely. The .MIPS.options section is only
> > emitted for
> > N64 currently but I don't see any technical reason why it cannot be
> > emitted for 32-bit or 64-bit ELFs so I would propose using it for all
> > 3 of the most used ABIs (O32, N32, N64). Perhaps going so far as to
> > stop emitting the .reginfo section for O32 and N32 as that information
> > fits into the .MIPS.options section anyway. The structure of this
> > section is quite neat in that the option descriptor header states the
> > total size of the header+payload and therefore can be easily skipped.
> > The maximum payload per descriptor is 255 bytes including the 8 byte
> > header but that does not seem restrictive. I would add a new
> > descriptor type and be able to re-use a reasonable amount of existing
> > code. There is a program header and dynamic tag already defined for
> > this section which are only emitted for irix currently but that is
> easily resolved.
> 
> How much code would it save though?  It means you don't have to define a
> new header, but that's only a small saving.

It's more that I realised the structure of the .MIPS.options section is
quite closely matched to what I need and its treatment throughout the tools
is precisely the way I would need a new section to behave. It's the special
cases needed to handle a new section type throughout the tools which would
end up not being needed. The section name is also likely to be very similar
to .MIPS.options i.e. .MIPS.flags.
 
> I agree it'd be possible to use .MIPS.options, but in some ways it seems
> a bit heavyweight for what we want.  Like with PT_NOTE, looking up the
> data would involve a search for the right kind.  In this case we'd be
> searching for an integer rather than a string, so it's much less of a
> problem, but it still seems like an unnecessary overhead.
> FWIW, looking at an n64 libc.so I have lying around, the .MIPS.options
> is 0xe948 bytes in size.

Indeed not small. 

> I suppose you could add a rule that the new kind needs to come first,
> but that doesn't seem in the spirit of the thing.  If we're adding
> special rules like that then we might as well add a new header instead.

My thinking was that we will be displacing the .MIPS.options or .reginfo
segment by introducing something new next to the program headers. Given
these segments are recommended to follow the program headers it could be
conflicting to say that a newly proposed segment also must follow the
program headers.

> Also, like you say, it would involve either a change to the ELF32 ABIs
> or having duplicated information there.  We'd probably have to go for
> the duplicated information since .reginfo is part of the psABI.

I don't particularly see that the reginfo descriptor has to be mandatory
for the ELF32 ABIs so we could avoid the duplication even not that it is
much of a saving.

I don't have enough knowledge of other architectures to know how common
special sections, segments, headers are but I get the feeling MIPS already
has more than most. Avoiding new ones (and/or propagating existing ones
to more ABIs) may reduce the confusion over the different ABIs.

I too am still open to ideas here but I do need to settle on something
quite soon.

If I start the implementation by extending .MIPS.options perhaps others
may chime in with their opinions... Moving to a different section should
only be the matter of duplicating the code for .MIPS.options so won't
cause too much pain if we settle on that.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-15 21:09                                   ` Matthew Fortune
@ 2014-04-16  8:25                                     ` Richard Sandiford
  2014-04-24 16:11                                       ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-16  8:25 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > I've been reviewing the .MIPS.options section to see whether it would
>> > be a suitable place for storing this new information and from what I
>> > can tell it would fit quite nicely. The .MIPS.options section is only
>> > emitted for
>> > N64 currently but I don't see any technical reason why it cannot be
>> > emitted for 32-bit or 64-bit ELFs so I would propose using it for all
>> > 3 of the most used ABIs (O32, N32, N64). Perhaps going so far as to
>> > stop emitting the .reginfo section for O32 and N32 as that information
>> > fits into the .MIPS.options section anyway. The structure of this
>> > section is quite neat in that the option descriptor header states the
>> > total size of the header+payload and therefore can be easily skipped.
>> > The maximum payload per descriptor is 255 bytes including the 8 byte
>> > header but that does not seem restrictive. I would add a new
>> > descriptor type and be able to re-use a reasonable amount of existing
>> > code. There is a program header and dynamic tag already defined for
>> > this section which are only emitted for irix currently but that is
>> easily resolved.
>> 
>> How much code would it save though?  It means you don't have to define a
>> new header, but that's only a small saving.
>
> It's more that I realised the structure of the .MIPS.options section is
> quite closely matched to what I need and its treatment throughout the tools
> is precisely the way I would need a new section to behave. It's the special
> cases needed to handle a new section type throughout the tools which would
> end up not being needed. The section name is also likely to be very similar
> to .MIPS.options i.e. .MIPS.flags.

But like you say, DT_MIPS_OPTIONS is currently only used for IRIX 6,
so I doubt any of the loaders we're likely to care about handle it yet.
And when we add the support, having a list of blocks that you need to
search is going to be more complicated than having a single block.

It might save a few lines in the linkers, but I don't think the saving
there would be too big and it doesn't seem like the right thing to optimise
for anyway.

To put it another way, what's in .MIPS.options at the moment isn't in
practice useful for non-IRIX loaders.  What we're adding is.  And program
headers already divide up the data nicely, so I don't we need need a
.MIPS.options-style grouping structure on top of that.

>> I suppose you could add a rule that the new kind needs to come first,
>> but that doesn't seem in the spirit of the thing.  If we're adding
>> special rules like that then we might as well add a new header instead.
>
> My thinking was that we will be displacing the .MIPS.options or .reginfo
> segment by introducing something new next to the program headers. Given
> these segments are recommended to follow the program headers it could be
> conflicting to say that a newly proposed segment also must follow the
> program headers.

Yeah, but that's just documentation. :-)  When adding something we get to
say how it interacts with existing features.

>> Also, like you say, it would involve either a change to the ELF32 ABIs
>> or having duplicated information there.  We'd probably have to go for
>> the duplicated information since .reginfo is part of the psABI.
>
> I don't particularly see that the reginfo descriptor has to be mandatory
> for the ELF32 ABIs so we could avoid the duplication even not that it is
> much of a saving.

.reginfo is needed in ET_RELs because it gives the value of GP0, which is
needed to handle GP-relative relocations correctly.  I think we'd need to
keep it for those at least.  Maybe it isn't as important for fully-linked
objects, but if we're keeping it for relocatable objects we might as well
keep it for all objects.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-07 12:20       ` Matthew Fortune
  2014-04-07 13:21         ` Richard Sandiford
@ 2014-04-22 17:05         ` Joseph S. Myers
  2014-04-23  7:36           ` Matthew Fortune
  1 sibling, 1 reply; 41+ messages in thread
From: Joseph S. Myers @ 2014-04-22 17:05 UTC (permalink / raw)
  To: Matthew Fortune; +Cc: Richard Sandiford, macro, binutils

On Mon, 7 Apr 2014, Matthew Fortune wrote:

> > What did you think about the idea of reusing the .gnu_attributes section
> > data as the segment data?  It seems simpler than the PT_NOTE format.
> 
> I have no major objection. Slight concern over defining more of the MIPS
> ABI around the 'gnu' vendor attributes which is a bit strange given the
> MIPS architecture is not restricted to gnu tools. Having said that, MIPS is
> already using these as critical parts of the ABI and following it through
> to load time information as well then seems reasonable.
> 
> In order to use the pre-existing section for GNU attributes I guess it
> would need to become SEC_ALLOC and get associated with the new program
> header. Perhaps call it PT_MIPS_ATTRIBUTES or PT_GNU_ATTRIBUTES if this
> were done for all architectures. And then somehow get that to be positioned
> immediately after the notes segment.

Making an allocated segment for attributes was previously rejected; see 
references in <http://www.eglibc.org/archives/patches/msg01019.html>.  I 
don't think anything significant has changed to affect that design 
principle.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-22 17:05         ` Joseph S. Myers
@ 2014-04-23  7:36           ` Matthew Fortune
  0 siblings, 0 replies; 41+ messages in thread
From: Matthew Fortune @ 2014-04-23  7:36 UTC (permalink / raw)
  To: Joseph Myers; +Cc: Richard Sandiford, macro, binutils

Joseph Myers <joseph@codesourcery.com> writes:
> On Mon, 7 Apr 2014, Matthew Fortune wrote:
> 
> > > What did you think about the idea of reusing the .gnu_attributes
> > > section data as the segment data?  It seems simpler than the PT_NOTE
> format.
> >
> > I have no major objection. Slight concern over defining more of the
> > MIPS ABI around the 'gnu' vendor attributes which is a bit strange
> > given the MIPS architecture is not restricted to gnu tools. Having
> > said that, MIPS is already using these as critical parts of the ABI
> > and following it through to load time information as well then seems
> reasonable.
> >
> > In order to use the pre-existing section for GNU attributes I guess it
> > would need to become SEC_ALLOC and get associated with the new program
> > header. Perhaps call it PT_MIPS_ATTRIBUTES or PT_GNU_ATTRIBUTES if
> > this were done for all architectures. And then somehow get that to be
> > positioned immediately after the notes segment.
> 
> Making an allocated segment for attributes was previously rejected; see
> references in <http://www.eglibc.org/archives/patches/msg01019.html>.  I
> don't think anything significant has changed to affect that design
> principle.

Later in this thread Richard and I essentially re-did that entire discussion
and also rejected making an allocated attributes segment. The current plan
is to add a new section with a new segment/program header for MIPS ABI
flags. I'm calling it .MIPS.abiflags, at least for now, and this section
contains a versioned structure including various useful data about what
the binary is. I'm getting close to a patch to send for initial comments.

dl-load.c in glibc will need an extra hook adding to allow a port to act
upon custom program headers and read extra data from the DSO. The new
segment will be close to the program headers but may well be outside of
the statically determined size that dl-open reads to start with.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-11 16:29                         ` Richard Sandiford
  2014-04-11 18:27                           ` Matthew Fortune
@ 2014-04-23 20:43                           ` Joseph S. Myers
  1 sibling, 0 replies; 41+ messages in thread
From: Joseph S. Myers @ 2014-04-23 20:43 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Matthew Fortune, macro, binutils, Rich Fuhler

On Fri, 11 Apr 2014, Richard Sandiford wrote:

> Perhaps also an ABI revision number, so that we can bump it when
> adding things that old loaders won't be able to handle correctly.

We already have such a system in binutils / glibc, whereby EI_ABIVERSION 
is set by ld and checked by ld.so, it's just bitrotten (apparently) in 
various ways.  See 
<https://sourceware.org/ml/libc-alpha/2014-01/msg00375.html> for my most 
recent notes on this.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-16  8:25                                     ` Richard Sandiford
@ 2014-04-24 16:11                                       ` Matthew Fortune
  2014-04-24 17:18                                         ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-04-24 16:11 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Hi Richard,

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

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

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

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

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

Regards,
Matthew

include/

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

bfd/

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

binutils/

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

elfcpp/

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

gas/

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

ld/

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

opcodes/

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

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

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

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

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

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

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

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

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

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-24 16:11                                       ` Matthew Fortune
@ 2014-04-24 17:18                                         ` Richard Sandiford
  2014-04-24 18:39                                           ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-24 17:18 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Thanks for the patch.  I'll try to review it in detail over the weekend,
but just some quick comments about the ABI:

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> 1) I'm not sure there is much value encoding the gpr_size as log2
> versus just having an enum for the two current widths. I'm not that
> bothered though so if you have a gut feeling it would be better log2
> then fine.

An enum is fine with me.  The only other obvious value would be 128
(r5900/r7900), which would presumably be enum value 3, so we end up
with log2 - 4 anyway.

> 2) I changed fpr_size to fp_abi. It gives some synergy with
> gnu_attributes and I have re-used the same values

OK, but...

> 3) I'm not sure we need to track FP register width specifically. In
> the end the MSA ABI is actually a no-change so recording MSA ABI is
> not necessary but recording the presence of the MSA ASE is useful. I'm
> suggesting we use this along with the fp_abi to determine the width of
> the fp registers.

...the reason I suggested an fpr size was that it allowed the MSA
register width to be separated from the ASE revision number.

Let's specify this on the assumption that there'll be a 256-bit MSA
in future.  What would be the best way of representing that?  I'd rather
not use the ASE field, since I think ASE compatibility should as far
as possible be a case of ORing the input ASEs together.

> 4) isa_ext and ases make direct use of the existing masks used by gas
> and I have moved all these to be part of the include/elf/mips.h
> header. This does leave one small annoyance which is that 64-bit
> versions of ASEs don't really need to be included in this mask but are
> for reasons internal to gas. I'm not sure what to do with this, I'm
> thinking some rework of tc-mips to avoid the need for the 64-bit
> versions of the ASEs in the mask.

I'll try to look at this over the weekend.  I agree we don't want to
expose the 64-bit flags outside binutils.

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

My main concern is about the isa_ext field being a bitmask rather than
an enum.  The reason we use a bitmask internally is because some instructions
are defined in the same way for more than one processor extension and using
a bitmask avoids duplicate opcode entries.  But I think the isa_ext field
is conceptually an enum and compatibility should just be a case of:

  0 comb X -> X
  X comb 0 -> X
  X comb X -> X
  error otherwise

AFAICT, for all current values, any isa_ext with two bits set would
either be impossible (because the two extensions aren't compatible) or
be redundant (because one extension is itself an extension of the other).

FWIW, the ABI side looks good to me otherwise at first glance.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-24 17:18                                         ` Richard Sandiford
@ 2014-04-24 18:39                                           ` Matthew Fortune
  2014-04-24 20:21                                             ` Maciej W. Rozycki
  2014-04-25  7:39                                             ` Richard Sandiford
  0 siblings, 2 replies; 41+ messages in thread
From: Matthew Fortune @ 2014-04-24 18:39 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > 1) I'm not sure there is much value encoding the gpr_size as log2
> > versus just having an enum for the two current widths. I'm not that
> > bothered though so if you have a gut feeling it would be better log2
> > then fine.
> 
> An enum is fine with me.  The only other obvious value would be 128
> (r5900/r7900), which would presumably be enum value 3, so we end up with
> log2 - 4 anyway.

I meant to ask you about your reference to r5900/r7900.  Is this the 'Emotion
Engine'? I've never seen the documentation for this but I see the references
in mips-opc.c about VU0 macromode instructions.
 
> > 2) I changed fpr_size to fp_abi. It gives some synergy with
> > gnu_attributes and I have re-used the same values
> 
> OK, but...
> 
> > 3) I'm not sure we need to track FP register width specifically. In
> > the end the MSA ABI is actually a no-change so recording MSA ABI is
> > not necessary but recording the presence of the MSA ASE is useful. I'm
> > suggesting we use this along with the fp_abi to determine the width of
> > the fp registers.
> 
> ...the reason I suggested an fpr size was that it allowed the MSA
> register width to be separated from the ASE revision number.
> 
> Let's specify this on the assumption that there'll be a 256-bit MSA in
> future.  What would be the best way of representing that?  I'd rather
> not use the ASE field, since I think ASE compatibility should as far as
> possible be a case of ORing the input ASEs together.

I don't have a big problem with fpr_size but the only thing we can use it
for is to track the widest required register. I implemented gpr_size to be
stricter as we can't mix 32-bit and 64-bit ABIs though I guess just
tracking widest would be OK there as well?

I think I've realised what scenario you are accounting for with MSA.
Something like... 'MSAv2' that supports more vector operations and also
256-bit registers but it is possible to use just the 128-bit subset and
make use of the new operations.

I'd propose changing the start of the structure to the following:

unsigned short version;
unsigned char isa_level; 
unsigned char isa_rev; 
unsigned char gpr_size; 
unsigned char cpr1_size; 
unsigned char cpr2_size;
unsigned char fp_abi;

> > 4) isa_ext and ases make direct use of the existing masks used by gas
> > and I have moved all these to be part of the include/elf/mips.h
> > header. This does leave one small annoyance which is that 64-bit
> > versions of ASEs don't really need to be included in this mask but are
> > for reasons internal to gas. I'm not sure what to do with this, I'm
> > thinking some rework of tc-mips to avoid the need for the 64-bit
> > versions of the ASEs in the mask.
> 
> I'll try to look at this over the weekend.  I agree we don't want to
> expose the 64-bit flags outside binutils.
> 
> > As before it would be useful to get comments on the ABI aspects of
> > this first. I will then update the FPXX specification accordingly when
> > the last few details are ironed out.
> 
> My main concern is about the isa_ext field being a bitmask rather than
> an enum.  The reason we use a bitmask internally is because some
> instructions are defined in the same way for more than one processor
> extension and using a bitmask avoids duplicate opcode entries.  But I
> think the isa_ext field is conceptually an enum and compatibility should
> just be a case of:
> 
>   0 comb X -> X
>   X comb 0 -> X
>   X comb X -> X
>   error otherwise
> 
> AFAICT, for all current values, any isa_ext with two bits set would
> either be impossible (because the two extensions aren't compatible) or
> be redundant (because one extension is itself an extension of the
> other).

I realised this after starting to change things.  I'd like to find a way to
use the same defines in the structure and gas internals if possible even if
one has to be derived rather than direct. I.e. construct a bit mask with
1 << AFL_EXT_something.

> FWIW, the ABI side looks good to me otherwise at first glance.

I'll start writing this up with the view that the structure may change a
little bit still.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-24 18:39                                           ` Matthew Fortune
@ 2014-04-24 20:21                                             ` Maciej W. Rozycki
  2014-04-25  7:39                                             ` Richard Sandiford
  1 sibling, 0 replies; 41+ messages in thread
From: Maciej W. Rozycki @ 2014-04-24 20:21 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: Richard Sandiford, Joseph Myers (joseph@codesourcery.com),
	binutils, Rich Fuhler

On Thu, 24 Apr 2014, Matthew Fortune wrote:

> > An enum is fine with me.  The only other obvious value would be 128
> > (r5900/r7900), which would presumably be enum value 3, so we end up with
> > log2 - 4 anyway.
> 
> I meant to ask you about your reference to r5900/r7900.  Is this the 'Emotion
> Engine'? I've never seen the documentation for this but I see the references
> in mips-opc.c about VU0 macromode instructions.

 Sony used the R5900 processor in PS2.  Here's a reference to 
documentation: http://sourceware.org/ml/binutils/2013-01/msg00187.html -- 
not readily available, but you might be able to ask Sony.

 The other processor is actually the TX79 by Toshiba.

  Maciej

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-24 18:39                                           ` Matthew Fortune
  2014-04-24 20:21                                             ` Maciej W. Rozycki
@ 2014-04-25  7:39                                             ` Richard Sandiford
  2014-05-01 14:43                                               ` Matthew Fortune
  1 sibling, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-04-25  7:39 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > 2) I changed fpr_size to fp_abi. It gives some synergy with
>> > gnu_attributes and I have re-used the same values
>> 
>> OK, but...
>> 
>> > 3) I'm not sure we need to track FP register width specifically. In
>> > the end the MSA ABI is actually a no-change so recording MSA ABI is
>> > not necessary but recording the presence of the MSA ASE is useful. I'm
>> > suggesting we use this along with the fp_abi to determine the width of
>> > the fp registers.
>> 
>> ...the reason I suggested an fpr size was that it allowed the MSA
>> register width to be separated from the ASE revision number.
>> 
>> Let's specify this on the assumption that there'll be a 256-bit MSA in
>> future.  What would be the best way of representing that?  I'd rather
>> not use the ASE field, since I think ASE compatibility should as far as
>> possible be a case of ORing the input ASEs together.
>
> I don't have a big problem with fpr_size but the only thing we can use it
> for is to track the widest required register. I implemented gpr_size to be
> stricter as we can't mix 32-bit and 64-bit ABIs though I guess just
> tracking widest would be OK there as well?

Yeah, it probably amounts to the same thing.  With EABI32 and EABI64 being
treated as separate ABI enums, the ABI check (which should happen first)
would catch mismatches.

Again, the only case I can think of where it could matter would be
for 128-bit GPRs, which could be used with existing ABIs thanks to
the way that the upper 64 bits weren't touched by the 32-bit and
64-bit load and arithmetic insns (IIRC, obviously this is going
back a few years).  In that case taking the widest would be correct.

> I think I've realised what scenario you are accounting for with MSA.
> Something like... 'MSAv2' that supports more vector operations and also
> 256-bit registers but it is possible to use just the 128-bit subset and
> make use of the new operations.

Right.

> I'd propose changing the start of the structure to the following:
>
> unsigned short version;
> unsigned char isa_level; 
> unsigned char isa_rev; 
> unsigned char gpr_size; 
> unsigned char cpr1_size; 
> unsigned char cpr2_size;
> unsigned char fp_abi;

Sounds good to me, thanks.

>> My main concern is about the isa_ext field being a bitmask rather than
>> an enum.  The reason we use a bitmask internally is because some
>> instructions are defined in the same way for more than one processor
>> extension and using a bitmask avoids duplicate opcode entries.  But I
>> think the isa_ext field is conceptually an enum and compatibility should
>> just be a case of:
>> 
>>   0 comb X -> X
>>   X comb 0 -> X
>>   X comb X -> X
>>   error otherwise

Realised later that this wasn't strictly true.  When one ISA extends
another, those two can be merged and take the maximum.

>> AFAICT, for all current values, any isa_ext with two bits set would
>> either be impossible (because the two extensions aren't compatible) or
>> be redundant (because one extension is itself an extension of the
>> other).
>
> I realised this after starting to change things.  I'd like to find a way to
> use the same defines in the structure and gas internals if possible even if
> one has to be derived rather than direct. I.e. construct a bit mask with
> 1 << AFL_EXT_something.

I agree using the same enum internally would be good if it's natural
and easy.  And maybe it would be.  The opcodes table uses an abbreviated
form of the INSN_* flags anyway.  But if it gets too convoluted then
having separate internal numbering would be fine too.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-04-25  7:39                                             ` Richard Sandiford
@ 2014-05-01 14:43                                               ` Matthew Fortune
  2014-05-02 20:31                                                 ` Doug Gilmore
  2014-05-06 19:19                                                 ` Richard Sandiford
  0 siblings, 2 replies; 41+ messages in thread
From: Matthew Fortune @ 2014-05-01 14:43 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

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

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-01 14:43                                               ` Matthew Fortune
@ 2014-05-02 20:31                                                 ` Doug Gilmore
  2014-05-06 11:57                                                   ` Matthew Fortune
  2014-05-06 19:19                                                 ` Richard Sandiford
  1 sibling, 1 reply; 41+ messages in thread
From: Doug Gilmore @ 2014-05-02 20:31 UTC (permalink / raw)
  To: Matthew Fortune, Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

[-- Attachment #1: Type: text/plain, Size: 520 bytes --]

On 05/01/2014 07:42 AM, Matthew Fortune wrote:
> 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
> 
Hi Matthew,

I had problems applying your patch, so I attached a copy that I could apply
cleanly.

Doug


[-- Attachment #2: 0001-Add-support-for-O32-FPXX-ABI.patch --]
[-- Type: text/x-patch, Size: 135755 bytes --]

From 1fee5487105e4c72d94a64b514deb02e6d0bc593 Mon Sep 17 00:00:00 2001
From: mfortune <matthew.fortune@imgtec.com>
Date: Sat, 29 Mar 2014 11:40:50 +0000
Subject: [PATCH] Add support for O32 FPXX ABI

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/as.texinfo                 |   14 +
 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 +--
 14 files changed, 1895 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);
+}
 \f
 /* 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;
 }
 \f
+/* 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..f63d31c 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
 };
 \f
 /* 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;
 }
 \f
-/* 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;
 }
 \f
@@ -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/as.texinfo b/gas/doc/as.texinfo
index d16bbf6..e028823 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -399,6 +399,7 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
    [@b{-g}[@var{debug level}]] [@b{-G} @var{num}] [@b{-KPIC}] [@b{-call_shared}]
    [@b{-non_shared}] [@b{-xgot} [@b{-mvxworks-pic}]
    [@b{-mabi}=@var{ABI}] [@b{-32}] [@b{-n32}] [@b{-64}] [@b{-mfp32}] [@b{-mgp32}]
+   [@b{-mfp64}] [@b{-mgp64}] [@b{-mfpxx}]
    [@b{-march}=@var{CPU}] [@b{-mtune}=@var{CPU}] [@b{-mips1}] [@b{-mips2}]
    [@b{-mips3}] [@b{-mips4}] [@b{-mips5}] [@b{-mips32}] [@b{-mips32r2}]
    [@b{-mips64}] [@b{-mips64r2}]
@@ -1308,6 +1309,19 @@ flags force a certain group of registers to be treated as 32 bits wide at
 all times.  @samp{-mgp32} controls the size of general-purpose registers
 and @samp{-mfp32} controls the size of floating-point registers.
 
+@item -mgp64
+@itemx -mfp64
+The register sizes are normally inferred from the ISA and ABI, but these
+flags force a certain group of registers to be treated as 64 bits wide at
+all times.  @samp{-mgp64} controls the size of general-purpose registers
+and @samp{-mfp64} controls the size of floating-point registers.
+
+@item -mfpxx
+The register sizes are normally inferred from the ISA and ABI, but using
+this flag in combination with @samp{-mabi=32} enables an ABI variant
+which will operate correctly with floating-point registers which are
+32 or 64 bits wide.
+
 @item -mips16
 @itemx -no-mips16
 Generate code for the MIPS 16 processor.  This is equivalent to putting
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
 \f
 /* 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.  */
 \f
 
 /* 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 364f267..52ec6bc 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
@@ -1108,73 +1109,6 @@ static const unsigned int mips_isa_table[] = {
 };
 #undef ISAF
 
-/* 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.  */
@@ -1234,75 +1168,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.12


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-02 20:31                                                 ` Doug Gilmore
@ 2014-05-06 11:57                                                   ` Matthew Fortune
  2014-05-06 12:40                                                     ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-05-06 11:57 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com),
	binutils, Rich Fuhler, Doug Gilmore

While testing the GCC patch for O32 FPXX I (luckily) hit a few issues and I
think that the way in which the FP ABI is inferred in GAS needs to be different
from the patch I posted. We've discussed this topic before and had some
differing opinions so I'll explain my current thinking and see what you think.

The problem I hit when testing was that I found some assembler modules that were
marked as FP32 even when built with -mfpxx. This was down to a specs bug in my
gcc patch that was not passing on -mfpxx to the assembler but this was good as
it turns out. If -mfpxx had been passed then the modules would have silently
been marked as FPXX and I wouldn't have found that this was wrong until runtime
which would be unpleasant to say the least. I therefore think that GAS should
only infer the default FP ABI unless explicitly told that it should infer a
different one. I've annotated the offending code below from tc-mips.c:

+  /* Only update the ABI if it is not already specified.  A .gnu_attribute
+     always wins.  */
+  if (fpabi == Val_GNU_MIPS_ABI_FP_ANY)
+    {

This remains important. We do not want to clobber an explicit .gnu_attribute.

+      /* 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;

This is OK since -msoft-float guarantees no floating point instructions are
enabled

+      /* 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;

This is not OK. As we don't know if the user wrote .module singlefloat or used
-msingle-float. If it was .module then this shows intent that the code in this
module is designed for single-float so that would be OK.

+      else if (mips_seen_fp_code == TRUE)
+	{
+	  switch (file_mips_opts.fp)
+	    {
+	    case 32:
+	      fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;

This is OK. It should be safe to say that all hand-crafted modules using 32-bit
floating point registers comply with the default double precision ABI.

+	      break;
+	    case 0:
+	      fpabi = Val_GNU_MIPS_ABI_FP_XX;

This is not OK. We don't know if it was .module fp=xx or -mfpxx. Same rationale
as single-float.

+	      break;
+	    case 64:
+	      if (file_mips_opts.gp32)
+		fpabi = Val_GNU_MIPS_ABI_FP_64;

This is not OK. We don't know if it was .module fp=64 or -mfp64. Same rationale
as single-float.

+	      else
+		fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;

This is OK. Same rationale as "case 32" above but for 64-bit ABIs.

+	      break;
+	    }
+	}
+
+      bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+				Tag_GNU_MIPS_ABI_FP, fpabi);
+    }

I propose a further option to fix this. -minfer-abi and .module infer-fpabi. The
updated code would look like this and will force assembly code writers who use
floating-point to think about the ABI or stick with the default.

  /* 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;
      else if (mips_seen_fp_code == TRUE)
        {
          if (!mips_infer_fpabi)
            fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
          else
            {
              /* 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.  */
              if (file_mips_opts.single_float == 1)
                fpabi = Val_GNU_MIPS_ABI_FP_SINGLE;
              else
                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);
    }

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-06 11:57                                                   ` Matthew Fortune
@ 2014-05-06 12:40                                                     ` Richard Sandiford
  2014-05-06 14:08                                                       ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-05-06 12:40 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com),
	binutils, Rich Fuhler, Doug Gilmore

BTW, sorry for the slow response to your original patch.  I got about
half way through a review yesterday, but we definitely need to change
the way that the current code reports FPU errors.  Just cutting-&-pasting
the meaning of each tag N times isn't good.  (I knew I should have
pushed back more than on that...)

So I'm planning to commit a patch to update the code that checks and
prints the tags.  Hopefully it should be easy to rebase yours on top.

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> +      /* 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;
>
> This is not OK. As we don't know if the user wrote .module singlefloat or used
> -msingle-float. If it was .module then this shows intent that the code in this
> module is designed for single-float so that would be OK.

But .module should act like the corresponding command-line option,
and vice versa.  I don't think any extra trust should be given to
one over the other.

I think we need to keep the interface as simple as possible.

> I propose a further option to fix this. -minfer-abi and .module infer-fpabi.

No!!! :-)  Even more options would just make things unnecessarily complicated.
Anything passing the command-line options or generating the .module should
say what the ABI is instead.

The general principle is that an asm file can include code that requires
features outside the module-level setting.  We should never infer
anything about the global FPU ABI from something like an ADD.D that
we happen to see.  It might be that the ADD.D is part of an ifunc
(or similar mechanism) that only runs when an FPU is available.

IMO we should just leave the FPU ABI as "any" unless a command-line option,
.module or .gnu_attribute says what the ABI actually is.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-06 12:40                                                     ` Richard Sandiford
@ 2014-05-06 14:08                                                       ` Matthew Fortune
  2014-05-06 14:39                                                         ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-05-06 14:08 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com),
	binutils, Rich Fuhler, Doug Gilmore

> So I'm planning to commit a patch to update the code that checks and
> prints the tags.  Hopefully it should be easy to rebase yours on top.

Fine with me. That code is/was insane. I was merely going along with it.
 
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > +      /* 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;
> >
> > This is not OK. As we don't know if the user wrote .module singlefloat
> or used
> > -msingle-float. If it was .module then this shows intent that the code
> in this
> > module is designed for single-float so that would be OK.
> 
> But .module should act like the corresponding command-line option,
> and vice versa.  I don't think any extra trust should be given to
> one over the other.
> 
> I think we need to keep the interface as simple as possible.

I agree, but we also need safety.

> > I propose a further option to fix this. -minfer-abi and .module infer-
> fpabi.
> 
> No!!! :-)  Even more options would just make things unnecessarily
> complicated.
> Anything passing the command-line options or generating the .module
> should
> say what the ABI is instead.

Are you saying you think the user should have to write .gnu_attribute 4,5 or .gnu_attribute 4,6 to get any FP ABI attribute other than 'any'? Your comment below suggests not...

> The general principle is that an asm file can include code that requires
> features outside the module-level setting.  We should never infer
> anything about the global FPU ABI from something like an ADD.D that
> we happen to see.  It might be that the ADD.D is part of an ifunc
> (or similar mechanism) that only runs when an FPU is available.
> 
> IMO we should just leave the FPU ABI as "any" unless a command-line
> option,
> .module or .gnu_attribute says what the ABI actually is.

My problem is with the command line options, not .module. The writer of the code has control over .module but not over the command line options. I.e. consider a toolchain prepared with --with-fp=xx and a toolchain prepared with --with-fp=32/default. Depending on the toolchain used then any hand-crafted assembly code may be tagged with an incompatible FP ABI. While we could validly assume that options like -mfpxx won't be used explicitly by people building code that they did not write... implied options from the toolchain mean that the compiler driver may use them. For FPXX to work we have to enable linux distributions to include a compiler that is FPXX by default to facilitate the transition but do so safely. Any thoughts on how to make this safe?

The code I referred to as being broken when testing GCC was mips16.S from libgcc. It is a niche case given its purpose but demonstrates the problems of pre-existing FP code.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-06 14:08                                                       ` Matthew Fortune
@ 2014-05-06 14:39                                                         ` Richard Sandiford
  0 siblings, 0 replies; 41+ messages in thread
From: Richard Sandiford @ 2014-05-06 14:39 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com),
	binutils, Rich Fuhler, Doug Gilmore

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > I propose a further option to fix this. -minfer-abi and .module infer-
>> fpabi.
>> 
>> No!!! :-)  Even more options would just make things unnecessarily
>> complicated.
>> Anything passing the command-line options or generating the .module
>> should
>> say what the ABI is instead.
>
> Are you saying you think the user should have to write .gnu_attribute
> 4,5 or .gnu_attribute 4,6 to get any FP ABI attribute other than 'any'?
> Your comment below suggests not...

The command line, .module and .gnu_attribute can all be used to set
the FP ABI.

>> The general principle is that an asm file can include code that requires
>> features outside the module-level setting.  We should never infer
>> anything about the global FPU ABI from something like an ADD.D that
>> we happen to see.  It might be that the ADD.D is part of an ifunc
>> (or similar mechanism) that only runs when an FPU is available.
>> 
>> IMO we should just leave the FPU ABI as "any" unless a command-line
>> option,
>> .module or .gnu_attribute says what the ABI actually is.
>
> My problem is with the command line options, not .module. The writer of
> the code has control over .module but not over the command line
> options. I.e. consider a toolchain prepared with --with-fp=xx and a
> toolchain prepared with --with-fp=32/default. Depending on the toolchain
> used then any hand-crafted assembly code may be tagged with an
> incompatible FP ABI. While we could validly assume that options like
> -mfpxx won't be used explicitly by people building code that they did
> not write... implied options from the toolchain mean that the compiler
> driver may use them. For FPXX to work we have to enable linux
> distributions to include a compiler that is FPXX by default to
> facilitate the transition but do so safely. Any thoughts on how to make
> this safe?

We fundamentally can't make it safe, in the sense that an asm file
without an explicit annotation by definition doesn't say what ABI
it uses.  The same problem occurs if using inline asms in a C compilation.
If you use a header file with floating-point inline asms, those asms
will be included in code whose .module or .gnu_attribute is dictated by
the GCC command-line options.  There's no guarantee that the inline asms
are safe for all such .modules or .gnu_attributes.  The problem isn't
specific to the assembler command line.

If command-line options are given, we need to assume that that's the
output that the user wants.  We can detect cases where there are
"obvious" breaches of the ABI outside of an appropriate push/pop, but
IMO it should be that way around: assume that the command-line, .module
or .gnu_attribute (in increasing order of precedence) specify the ABI
correctly and report a diagnostic when the code seems to be something
else.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-01 14:43                                               ` Matthew Fortune
  2014-05-02 20:31                                                 ` Doug Gilmore
@ 2014-05-06 19:19                                                 ` Richard Sandiford
  2014-05-06 22:47                                                   ` Maciej W. Rozycki
  2014-05-06 22:51                                                   ` Matthew Fortune
  1 sibling, 2 replies; 41+ messages in thread
From: Richard Sandiford @ 2014-05-06 19:19 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Thanks for all the work, mostly looks good to me FWIW.

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> +/* 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);
> +}

OK, this is preexisting code, but I'm not sure it's right.
32bitness should be taken purely from the ABI, not the architecture,
otherwise we could treat 32-bit MIPS64r2 code differently from MIPS32r2
code.

Since o32 was the original (with no flags), I think we should have
mips_64bit_flags_p instead.

OTOH that should be done separately from your patch, so never mind.

> +/* 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;
> +

Would feel more comfortable with a clearing memset here, and leave
the explicit 0s out.

> +  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;

This can be set from EF_MIPS_MACH, at least to some extent.

> @@ -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;
> +
> +	}

Excess blank line.

> @@ -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;
> +	}

I imagine this is right, but which case does it handle?  Links with
no MIPS ELF inputs?

> @@ -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);

Minor nit, but "abiflags" vs. "iabiflags" is a bit hard to follow,
since the "i" could be "implicit" (but I assume is "input").

> +  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;

isa_ext should be handled by bfd_set_arch_mach.

> @@ -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);

Hmm, wasn't the plan to get rid of the 64-bit versions?  I think the external
interface really shouldn't have this distinction.  If we can do that while
still using the AFL_* numbering internally then great, but if something
has to give, it should be having a different external and internal numbering.

> +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);

As I said before, I think this should be an enum rather than a bitmask.

> +/* 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
>  };

Let's not duplicate the comments like this -- please fuse them together
more naturally.

> @@ -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;

Warnings should start with lower case (the MIPS code should be consistent
about that now, although the target-independent code isn't always).
Please use %s even where only one incompatibility exists (single and
soft float).  Also, please use the "not `%s'" version when the ABI
wasn't o32 but needed to be.

> +/* 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"));

You said before that -mfp64 and -msingle-float were compatible, but I'm not
sure they are really.  I think that should be an error too.

> +      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");

I don't think we should warn about code that's compatible with the
current ".set fp".  How would you write a warning-free fp=64 ifunc?

> @@ -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))

We should keep the bit about forcing it to memory otherwise.
Can you explain the HAVE_32BIT_FPRS && mips_opts.fp != 0 thing a bit more?
Maybe we should get rid of HAVE_32BIT_FPRS and HAVE_64BIT_FPRS now that
there are three alternatives and just test mips_opts.fp directly.
(Please do that as a separate patch though -- there are quite a few
different things going on in the current patch.)

> @@ -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

Would be great if you could split out the ASE renaming into a separate
patch too.

> @@ -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:

I think this consolidation into file_mips_opts should be a separate patch too
(without changing the logic).

> @@ -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)

s_foo is just for directives -- let's call this parse_code_option or
something (suggestions for better names welcome).

> @@ -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;

Seems more natural as:

	  if (mips_opts.fp != 0)
	    {
	      if (mips_opts.arch == CPU_R5900)
		mips_opts.fp = 32;
	      else
		mips_opts.fp = 64;
	    }

> +/* 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);

!foo rather than foo == FALSE (throughout)

> +
> +      /* 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"));

I think this should be an as_bad.

> +  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"));

I think we should do this unconditionally when checking the options
rather than here.

> @@ -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;
> +}

I think this should be a separate patch too.

> 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.

...floating-point registers...?

> +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

Nit: module-level

> +a warning if found after the first instruction, @code{.set} directive or
> +any code generating directive.

If it becomes a hard error we can remove the "and will raise..." bit.

> @@ -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.

found the last sentence hard to parse: s/across/use/, or something?

> +@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.

I'm not sure about the sizeof (double) thing: I think it's legitimate
to use -msingle-float with a 32-bit FPU and use software libraries
for double-precision.

> +@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.

Obviously the "and instruction usage" is the contentious bit here :-)

I think it'd be better to change gp32 into gp too, for consistency.
I can do that as a follow-up though.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-06 19:19                                                 ` Richard Sandiford
@ 2014-05-06 22:47                                                   ` Maciej W. Rozycki
  2014-05-06 22:51                                                   ` Matthew Fortune
  1 sibling, 0 replies; 41+ messages in thread
From: Maciej W. Rozycki @ 2014-05-06 22:47 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: Matthew Fortune, Joseph Myers (joseph@codesourcery.com),
	binutils, Rich Fuhler

On Tue, 6 May 2014, Richard Sandiford wrote:

> > +/* 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);
> > +}
> 
> OK, this is preexisting code, but I'm not sure it's right.
> 32bitness should be taken purely from the ABI, not the architecture,
> otherwise we could treat 32-bit MIPS64r2 code differently from MIPS32r2
> code.
> 
> Since o32 was the original (with no flags), I think we should have
> mips_64bit_flags_p instead.

 FWIW, it was my first thought too upon spotting this addition, before I 
realised it's really not one, and this change merely moves the function 
around intact.  Upon further checking this code seems to try mimicking 
what GAS does in mips_after_parse_args:

  /* 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)
	  || mips_abi == O32_ABI))
    mips_32bitmode = 1;

and is really very old.  Note that in this case where ISA_HAS_64BIT_REGS 
and `mips_abi == NO_ABI' are both true (the latter condition being a rare 
corner case, possible for ELF and I think IRIX 5 toolchains only where 
MIPS_DEFAULT_ABI is NO_ABI) GAS will set EF_MIPS_32BITMODE according to 
-mgp32/-mgp64 and the ABI bits will remain unset.

 E_MIPS_ABI_O32 is also omitted entirely on IRIX, according to 
USE_E_MIPS_ABI_O32, so we may want to consider defining ABI_O32_P like 
below instead:

/* 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
   || (!ABI_64_P (abfd)
       && (elf_elfheader (abfd)->e_flags & (EF_MIPS_ABI | EF_MIPS_ABI2)) == 0))

though I'm afraid the answer from this predicate won't be accurate either, 
for a different set of cases.

> > @@ -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);
> 
> Minor nit, but "abiflags" vs. "iabiflags" is a bit hard to follow,
> since the "i" could be "implicit" (but I assume is "input").

 `in_abiflags' perhaps? -- following `in_attr' and `out_attr' from 
mips_elf_merge_obj_attributes.

  Maciej

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-06 19:19                                                 ` Richard Sandiford
  2014-05-06 22:47                                                   ` Maciej W. Rozycki
@ 2014-05-06 22:51                                                   ` Matthew Fortune
  2014-05-07  8:01                                                     ` Richard Sandiford
  1 sibling, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-05-06 22:51 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Thanks for all the work, mostly looks good to me FWIW.
> 
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> > +/* 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);
> > +}
> 
> OK, this is preexisting code, but I'm not sure it's right.
> 32bitness should be taken purely from the ABI, not the architecture,
> otherwise we could treat 32-bit MIPS64r2 code differently from MIPS32r2
> code.
> 
> Since o32 was the original (with no flags), I think we should have
> mips_64bit_flags_p instead.
> 
> OTOH that should be done separately from your patch, so never mind.

I was uneasy about this code as well. I think I concluded it was right
because the 32BITMODE flag gets set if a 32-bit ABI is used with a
64-bit architecture. I can't hold all the intricacies of the flags in
my head so have to read all the comments each time to check.
 
> > +/* 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;
> > +
> 
> Would feel more comfortable with a clearing memset here, and leave
> the explicit 0s out.

Almost did that :-) No idea why I didn't in the end.
 
> > +  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;
> 
> This can be set from EF_MIPS_MACH, at least to some extent.

Yes.
 
> > @@ -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;
> > +
> > +	}
> 
> Excess blank line.
> 
> > @@ -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;
> > +	}
> 
> I imagine this is right, but which case does it handle?  Links with
> no MIPS ELF inputs?

It is both links with no MIPS ELF inputs and those with only empty inputs.
It is debatable as to what the end result would be from inferring flags
on such abfd as the e_flags may well be meaningless. The one thing that
will hold true is that .MIPS.abiflags and e_flags will be consistent.
 
> > @@ -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);
> 
> Minor nit, but "abiflags" vs. "iabiflags" is a bit hard to follow,
> since the "i" could be "implicit" (but I assume is "input").

Agreed. I was torn between short variables and keeping abiflags in
the name. Perhaps if I just used 'inferred' for the inferred flags.
The 'i' in iabiflags is supposed to mirror the other variables
associated with either (i)nput or (o)utput.

> > +  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;
> 
> isa_ext should be handled by bfd_set_arch_mach.

Are you saying we should ignore the isa_ext inputs and just use the new
overall arch to set this? The potential problem with bfd_set_arch_mach
is that it is called in a variety of different places and it can't
set just the isa_ext field and then mark the abiflags as valid. I'm
probably being dumb but I'm not sure how/where to hook this function
either? I haven't quite got my head around BFD target vectors.

> > @@ -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);
> 
> Hmm, wasn't the plan to get rid of the 64-bit versions?  I think the external
> interface really shouldn't have this distinction.  If we can do that while
> still using the AFL_* numbering internally then great, but if something
> has to give, it should be having a different external and internal numbering.

I think I'm going to have to give in and have different numbering for internal
and external for both ASEs and ISA extensions.

> > +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);
> 
> As I said before, I think this should be an enum rather than a bitmask.

That's fine. I've concluded this one needs to be a different numbering
scheme for the ABI so I'll do an enum style.

> > +/* 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
> >  };
> 
> Let's not duplicate the comments like this -- please fuse them together
> more naturally.

OK. I expect you are only referring to large comments not the inline comments
For field names.

> > @@ -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;
> 
> Warnings should start with lower case (the MIPS code should be consistent
> about that now, although the target-independent code isn't always).
> Please use %s even where only one incompatibility exists (single and
> soft float).  Also, please use the "not `%s'" version when the ABI
> wasn't o32 but needed to be.

OK. 

> > +/* 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"));
> 
> You said before that -mfp64 and -msingle-float were compatible, but I'm not
> sure they are really.  I think that should be an error too.

This area is fuzzy at best. I've seen evidence both ways round in binutils
(I can't point it out right now though) but GCC certainly only supports
-mfp32 -msingle-float so I'd be happy with enforcing that in gas and
dealing with any potential issues.

> > +      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");
> 
> I don't think we should warn about code that's compatible with the
> current ".set fp".  How would you write a warning-free fp=64 ifunc?

This is perhaps a non-intuitive piece of code but the mips_defer_fp_warn flag
will only be set if the overall module was not FPXX, the current mode is
not FPXX and the two do not match. The only way to support having both FP32
and FP64 in the same module is if the overall module is FPXX. The warning handles
the case of 'xx' when in reality it can never happen. It can only print fp32,fp64
or fp64,fp32. The reason the overall module must be FPXX (or matching the current
mode) is because that is the only way you get the choice of mode at runtime (FPXX)
or the correct mode to start with (FP64).

The specific example of fp=64 ifunc:

.module fp=xx

.set push
.set fp=64
add.s $f1, $f1, $f1
.set pop


> > @@ -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))
> 
> We should keep the bit about forcing it to memory otherwise.
> Can you explain the HAVE_32BIT_FPRS && mips_opts.fp != 0 thing a bit more?
> Maybe we should get rid of HAVE_32BIT_FPRS and HAVE_64BIT_FPRS now that
> there are three alternatives and just test mips_opts.fp directly.
> (Please do that as a separate patch though -- there are quite a few
> different things going on in the current patch.)

The final clause is to allow 64-bit constant generation when the value would
need to be moved to FPRs using a pair of MTC1s (not MTHC1). I retained the
HAVE_32BIT_FPRS clause as it expands to more than just fp != 64, but I don't
understand why it is more complex than that. The fp != 0 could just as easily
have been fp == 32 which is probably clearer and is there to ensure that
we don't break the FPXX ABI by using an MTC1 for the high part of a 64-bit
value.

There are still only two widths of register. The fact we are targeting FPXX
is only relevant in a small number of places, otherwise it is simply 32-bit
registers. So I would be changing HAVE_32BIT_FPRS to fp != 64 anyway.

> > @@ -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
> 
> Would be great if you could split out the ASE renaming into a separate
> patch too.

I will either undo this or do a separate patch to somehow rid the internal
implementation of needing 64-bit ASEs in the same mask as the real ASEs.

> > @@ -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:
> 
> I think this consolidation into file_mips_opts should be a separate patch too
> (without changing the logic).

That's fine. I wanted to take advice on splitting this up, I wanted to avoid
renaming the fp32 field in a separate patch to consolidating things.

> > @@ -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)
> 
> s_foo is just for directives -- let's call this parse_code_option or
> something (suggestions for better names welcome).

OK.

> > @@ -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;
> 
> Seems more natural as:
> 
> 	  if (mips_opts.fp != 0)
> 	    {
> 	      if (mips_opts.arch == CPU_R5900)
> 		mips_opts.fp = 32;
> 	      else
> 		mips_opts.fp = 64;
> 	    }
> 

OK.

> > +/* 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);
> 
> !foo rather than foo == FALSE (throughout)

OK.

> > +
> > +      /* 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"));
> 
> I think this should be an as_bad.

OK.

> > +  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"));
> 
> I think we should do this unconditionally when checking the options
> rather than here.

OK. I was cautious as I didn't know if there would be any side effects
of calling bfd_set_arch_mach unnecessarily. 

> > @@ -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;
> > +}
> 
> I think this should be a separate patch too.

OK. 
 
> > 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.
> 
> ...floating-point registers...?

Indeed. 

> > +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
> 
> Nit: module-level

OK. 

> > +a warning if found after the first instruction, @code{.set} directive or
> > +any code generating directive.
> 
> If it becomes a hard error we can remove the "and will raise..." bit.

I guess it is clear enough to just say "appear first in assembly" in hindsight.

> > @@ -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.
> 
> found the last sentence hard to parse: s/across/use/, or something?

Yes, clearly missing a word but changing to 'use' works.

> > +@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.
> 
> I'm not sure about the sizeof (double) thing: I think it's legitimate
> to use -msingle-float with a 32-bit FPU and use software libraries
> for double-precision.

Granted. But I believe the intent of the existing single-precision ABI is
that it is also -fshort-double in GCC terms (r5900 I believe). We would
need to track both single-float and single-float short-double separately.
There are no current plans (from us at least) to support just
single-precision with softfloat doubles.  The R6 spec re-introduces the
idea of a single precision only FPU and this is planned to be primarily
supported with short-doubles.
 
> > +@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.
> 
> Obviously the "and instruction usage" is the contentious bit here :-)

If we go with not having any safety net for hand written assembler then 
instruction usage does still need to be included in this decision in order
to support getting modules that end up as FP 'ANY' i.e. no-float. Otherwise
we must write-off the ability to generate modules without a specific FP ABI
because there is no way to detect if a user has written .gnu_attribute 4,0
nor is there a command-line/module option to assert it.

Any thoughts on what to do with attribute 4,0? Since it is not very safe
in GCC terms then we could just not support it I suppose.

I expect you can guess that I'm still not convinced by providing no safety net
at all for hand-written code :-) But I do understand the desire to keep things
clear cut. The original plan would have avoided this issue as it defined new
directives to get into fpxx. Perhaps it won't be an issue but I really wanted
to be able to provide distribution maintainers with a tool to detect which
objects remained FP32 even after the transition to an FPXX toolchain.

> I think it'd be better to change gp32 into gp too, for consistency.
> I can do that as a follow-up though.

Almost did that but stuck to necessary changes.

Thanks for getting through the patch, it was a monster. I'll post a split
patches soon.

Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-06 22:51                                                   ` Matthew Fortune
@ 2014-05-07  8:01                                                     ` Richard Sandiford
  2014-05-07 13:07                                                       ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-05-07  8:01 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> > +  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;
>> 
>> isa_ext should be handled by bfd_set_arch_mach.
>
> Are you saying we should ignore the isa_ext inputs and just use the new
> overall arch to set this?

Maybe bfd_set_arch_mach isn't the best place, but the reason I suggested it
was that the bfd_mach of the inputs should already reflect the architecture
in the input abiflags.  That might mean adding some more bfd_machs if there
isn't one for every AFL_EXT_*.

The bfd_mach is also used to perform the compatibility check
(mips_mach_extends_p in the code quoted above).  If the input bfd_mach isn't
correct at that point then we're not going to do the check properly.

> The potential problem with bfd_set_arch_mach is that it is called in a
> variety of different places and it can't set just the isa_ext field
> and then mark the abiflags as valid. I'm probably being dumb but I'm
> not sure how/where to hook this function either? I haven't quite got
> my head around BFD target vectors.

OK, I hadn't thought about the partial initialisation problem.
In that case I agree it might be better to keep it here.  But setting
the output isa_ext (and isa_level and isa_rev?) should at least be done 
when calling bfd_set_arch_mach in the code above.  Please put the code
in a helper function though.

>> > +/* 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
>> >  };
>> 
>> Let's not duplicate the comments like this -- please fuse them together
>> more naturally.
>
> OK. I expect you are only referring to large comments not the inline comments
> For field names.

Yeah, sorry, it was the duplication of the "Note ..." part.

>> > +/* 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"));
>> 
>> You said before that -mfp64 and -msingle-float were compatible, but I'm not
>> sure they are really.  I think that should be an error too.
>
> This area is fuzzy at best. I've seen evidence both ways round in binutils
> (I can't point it out right now though) but GCC certainly only supports
> -mfp32 -msingle-float so I'd be happy with enforcing that in gas and
> dealing with any potential issues.

Thanks.

>> > +      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");
>> 
>> I don't think we should warn about code that's compatible with the
>> current ".set fp".  How would you write a warning-free fp=64 ifunc?
>
> This is perhaps a non-intuitive piece of code but the mips_defer_fp_warn flag
> will only be set if the overall module was not FPXX, the current mode is
> not FPXX and the two do not match. The only way to support having both FP32
> and FP64 in the same module is if the overall module is FPXX. The
> warning handles
> the case of 'xx' when in reality it can never happen. It can only print
> fp32,fp64
> or fp64,fp32. The reason the overall module must be FPXX (or matching
> the current
> mode) is because that is the only way you get the choice of mode at
> runtime (FPXX)
> or the correct mode to start with (FP64).

So the warning is about cases like ".module fp=32 .... .set fp=64 .... "
and ".module fp=64 .... .set fp=32 .... "?  I still think that if the user
has code in a .set fp=NN block, we should assume that they really do want
to write some code for NN-bit FPRs.

Why does the oddness of the register matter in these cases?  I can see
why it would matter for xx because of the special call-save/call-clobber
rules, but why is it as special when the FPR sizes of the module and the
.set block are known?  E.g. using an even FPR in fp=32 code is going to
clobber more registers than it would in fp=64 mode.

If we're going to treat this as a problem, I think we should do it when
the ".set" is first encountered.

>> > @@ -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))
>> 
>> We should keep the bit about forcing it to memory otherwise.
>> Can you explain the HAVE_32BIT_FPRS && mips_opts.fp != 0 thing a bit more?
>> Maybe we should get rid of HAVE_32BIT_FPRS and HAVE_64BIT_FPRS now that
>> there are three alternatives and just test mips_opts.fp directly.
>> (Please do that as a separate patch though -- there are quite a few
>> different things going on in the current patch.)
>
> The final clause is to allow 64-bit constant generation when the value would
> need to be moved to FPRs using a pair of MTC1s (not MTHC1). I retained the
> HAVE_32BIT_FPRS clause as it expands to more than just fp != 64, but I don't
> understand why it is more complex than that. The fp != 0 could just as easily
> have been fp == 32 which is probably clearer and is there to ensure that
> we don't break the FPXX ABI by using an MTC1 for the high part of a 64-bit
> value.
>
> There are still only two widths of register. The fact we are targeting FPXX
> is only relevant in a small number of places, otherwise it is simply 32-bit
> registers. So I would be changing HAVE_32BIT_FPRS to fp != 64 anyway.

That's fine.  I still think it's clearer, since it's fairly obvious that
fp != 64 means "not fp=64 mode", whereas it isn't as obvious that
HAVE_32BIT_FPRS means "fp=32 or fp=xx" mode.  Having to combine checks
for HAVE_NNBIT_FPRS and mips_opts.fp seems a bit weird.

I'd missed that HAVE_32BIT_FPRS depends on the ISA.  I'm not sure that's
a good idea either, but if we have to keep it, maybe a macro like FPR_SIZE
would be better than direct checks for mips_opts.fp.

>> > +  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"));
>> 
>> I think we should do this unconditionally when checking the options
>> rather than here.
>
> OK. I was cautious as I didn't know if there would be any side effects
> of calling bfd_set_arch_mach unnecessarily. 

Sorry, when I said "checking the options" I meant "checking the
file-level options".  That should happen exactly once per assembly and
seems the right time to set the bfd_mach on the result.

>> > +@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.
>> 
>> I'm not sure about the sizeof (double) thing: I think it's legitimate
>> to use -msingle-float with a 32-bit FPU and use software libraries
>> for double-precision.
>
> Granted. But I believe the intent of the existing single-precision ABI is
> that it is also -fshort-double in GCC terms (r5900 I believe). We would
> need to track both single-float and single-float short-double separately.
> There are no current plans (from us at least) to support just
> single-precision with softfloat doubles.  The R6 spec re-introduces the
> idea of a single precision only FPU and this is planned to be primarily
> supported with short-doubles.

But emulated double-precision is definitely supported (and IIRC was also
used for r5900 GNU/Linux).  Maybe:

   Any double-precision operations must be emulated by software.

instead?

>> > +@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.
>> 
>> Obviously the "and instruction usage" is the contentious bit here :-)
>
> If we go with not having any safety net for hand written assembler then 
> instruction usage does still need to be included in this decision in order
> to support getting modules that end up as FP 'ANY' i.e. no-float. Otherwise
> we must write-off the ability to generate modules without a specific FP ABI
> because there is no way to detect if a user has written .gnu_attribute 4,0
> nor is there a command-line/module option to assert it.

If no attributes or command-line options are given, we can use the default
command-line setting for the configuration, which I think at the moment
is always -mdouble-float.  This will give a specific ABI.

This is just like the way that a file assembled without any other
information will get the default ISA level and CPU.

> Any thoughts on what to do with attribute 4,0? Since it is not very safe
> in GCC terms then we could just not support it I suppose.

We need a way for code that doesn't use FP to say that it doesn't care
about the FPU setting, so 4,0 seems fine to me.

> I expect you can guess that I'm still not convinced by providing no safety net
> at all for hand-written code :-) But I do understand the desire to keep things
> clear cut. The original plan would have avoided this issue as it defined new
> directives to get into fpxx. Perhaps it won't be an issue but I really wanted
> to be able to provide distribution maintainers with a tool to detect which
> objects remained FP32 even after the transition to an FPXX toolchain.

To be clear, I'm all in favour of warning about code that is wrong according
to the current mips_opts.  My objections were about not trusting ".set"
and about having complicated attempts to infer the ABI.

If the code is modern enough to use .gnu_attribute, we'll warn or error
(maybe the latter is best) about it being incompatible with -mfpxx.
If the code isn't modern enough to use .gnu_attribute and doesn't use
.set fp=, we should warn about anything that is obviously incompatible
with fp=xx (although clearly the warning can't be perfect).  In those
two cases we have the safety net.

If the code doesn't use .gnu_attribute but does use .set fp=, we simply
can't tell it apart from code is validly assuming a particular FPR size
(after appropriate checking).

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-07  8:01                                                     ` Richard Sandiford
@ 2014-05-07 13:07                                                       ` Matthew Fortune
  2014-05-07 13:38                                                         ` Richard Sandiford
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Fortune @ 2014-05-07 13:07 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> >> > +  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;
> >>
> >> isa_ext should be handled by bfd_set_arch_mach.
> >
> > Are you saying we should ignore the isa_ext inputs and just use the
> new
> > overall arch to set this?
> 
> Maybe bfd_set_arch_mach isn't the best place, but the reason I suggested
> it
> was that the bfd_mach of the inputs should already reflect the
> architecture
> in the input abiflags.  That might mean adding some more bfd_machs if
> there
> isn't one for every AFL_EXT_*.
>
> The bfd_mach is also used to perform the compatibility check
> (mips_mach_extends_p in the code quoted above).  If the input bfd_mach
> isn't
> correct at that point then we're not going to do the check properly.

OK.
 
> > The potential problem with bfd_set_arch_mach is that it is called in a
> > variety of different places and it can't set just the isa_ext field
> > and then mark the abiflags as valid. I'm probably being dumb but I'm
> > not sure how/where to hook this function either? I haven't quite got
> > my head around BFD target vectors.
> 
> OK, I hadn't thought about the partial initialisation problem.
> In that case I agree it might be better to keep it here.  But setting
> the output isa_ext (and isa_level and isa_rev?) should at least be done
> when calling bfd_set_arch_mach in the code above.  Please put the code
> in a helper function though.

OK

<snip>

> >> > +      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");
> >>
> >> I don't think we should warn about code that's compatible with the
> >> current ".set fp".  How would you write a warning-free fp=64 ifunc?
> >
> > This is perhaps a non-intuitive piece of code but the
> mips_defer_fp_warn flag
> > will only be set if the overall module was not FPXX, the current mode
> is
> > not FPXX and the two do not match. The only way to support having both
> FP32
> > and FP64 in the same module is if the overall module is FPXX. The
> > warning handles
> > the case of 'xx' when in reality it can never happen. It can only
> print
> > fp32,fp64
> > or fp64,fp32. The reason the overall module must be FPXX (or matching
> > the current
> > mode) is because that is the only way you get the choice of mode at
> > runtime (FPXX)
> > or the correct mode to start with (FP64).
> 
> So the warning is about cases like ".module fp=32 .... .set fp=64 .... "
> and ".module fp=64 .... .set fp=32 .... "?  I still think that if the
> user
> has code in a .set fp=NN block, we should assume that they really do
> want
> to write some code for NN-bit FPRs.

The check is there because the code simply has no chance at all of getting
the correct mode at runtime if the module mode is not FPXX.

> Why does the oddness of the register matter in these cases?  I can see
> why it would matter for xx because of the special call-save/call-clobber
> rules, but why is it as special when the FPR sizes of the module and the
> .set block are known?  E.g. using an even FPR in fp=32 code is going to
> clobber more registers than it would in fp=64 mode.

If you strictly stick to accessing even numbered registers then the code
will run correctly on FR=0 or FR=1 and the subset of the three calling
conventions you can then use are all identical. However...

> If we're going to treat this as a problem, I think we should do it when
> the ".set" is first encountered.

I'm fine with doing this instead, there is little value in allowing a
.set fp=[32|64] when the region of code it encompasses does not really
use any floating-point or uses a subset of floating-point.

> >> > @@ -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))
> >>
> >> We should keep the bit about forcing it to memory otherwise.
> >> Can you explain the HAVE_32BIT_FPRS && mips_opts.fp != 0 thing a bit
> more?
> >> Maybe we should get rid of HAVE_32BIT_FPRS and HAVE_64BIT_FPRS now
> that
> >> there are three alternatives and just test mips_opts.fp directly.
> >> (Please do that as a separate patch though -- there are quite a few
> >> different things going on in the current patch.)
> >
> > The final clause is to allow 64-bit constant generation when the value
> would
> > need to be moved to FPRs using a pair of MTC1s (not MTHC1). I retained
> the
> > HAVE_32BIT_FPRS clause as it expands to more than just fp != 64, but I
> don't
> > understand why it is more complex than that. The fp != 0 could just as
> easily
> > have been fp == 32 which is probably clearer and is there to ensure
> that
> > we don't break the FPXX ABI by using an MTC1 for the high part of a
> 64-bit
> > value.
> >
> > There are still only two widths of register. The fact we are targeting
> FPXX
> > is only relevant in a small number of places, otherwise it is simply
> 32-bit
> > registers. So I would be changing HAVE_32BIT_FPRS to fp != 64 anyway.
> 
> That's fine.  I still think it's clearer, since it's fairly obvious that
> fp != 64 means "not fp=64 mode", whereas it isn't as obvious that
> HAVE_32BIT_FPRS means "fp=32 or fp=xx" mode.  Having to combine checks
> for HAVE_NNBIT_FPRS and mips_opts.fp seems a bit weird.
> 
> I'd missed that HAVE_32BIT_FPRS depends on the ISA.  I'm not sure that's
> a good idea either, but if we have to keep it, maybe a macro like
> FPR_SIZE
> would be better than direct checks for mips_opts.fp.

I'd change the definition of HAVE_32BIT_FPRS if I was sure enough that it
wouldn't break things but I can't tell.

Some of this ugliness comes out of the fact that we have three sources
of information about FP state: singlefloat, softfloat and fp. Merging
these three together to give a single floating-point status may be a good
idea but I don't really want to do that in this patch.

Am I OK to just change this:
+	  || (HAVE_32BIT_FPRS && mips_opts.fp != 0))
To either:
+	  || (HAVE_32BIT_FPRS && mips_opts.fp == 32))
Or more boldly
+	  || (mips_opts.fp == 32))


> >> > +  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"));
> >>
> >> I think we should do this unconditionally when checking the options
> >> rather than here.
> >
> > OK. I was cautious as I didn't know if there would be any side effects
> > of calling bfd_set_arch_mach unnecessarily.
> 
> Sorry, when I said "checking the options" I meant "checking the
> file-level options".  That should happen exactly once per assembly and
> seems the right time to set the bfd_mach on the result.

Indeed it will only happen once anyway.

> >> > +@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.
> >>
> >> I'm not sure about the sizeof (double) thing: I think it's legitimate
> >> to use -msingle-float with a 32-bit FPU and use software libraries
> >> for double-precision.
> >
> > Granted. But I believe the intent of the existing single-precision ABI
> is
> > that it is also -fshort-double in GCC terms (r5900 I believe). We
> would
> > need to track both single-float and single-float short-double
> separately.
> > There are no current plans (from us at least) to support just
> > single-precision with softfloat doubles.  The R6 spec re-introduces
> the
> > idea of a single precision only FPU and this is planned to be
> primarily
> > supported with short-doubles.
> 
> But emulated double-precision is definitely supported (and IIRC was also
> used for r5900 GNU/Linux).  Maybe:
> 
>    Any double-precision operations must be emulated by software.
> 
> instead?

OK. But we will need to introduce a further FP ABI for short-double as part
of R6 (bare metal only) then. I haven't looked to see what the hybrid
calling convention ends up as for single-float (without short-double) I
wonder what happens to doubles.
 
> >> > +@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.
> >>
> >> Obviously the "and instruction usage" is the contentious bit here :-)
> >
> > If we go with not having any safety net for hand written assembler
> then
> > instruction usage does still need to be included in this decision in
> order
> > to support getting modules that end up as FP 'ANY' i.e. no-float.
> Otherwise
> > we must write-off the ability to generate modules without a specific
> FP ABI
> > because there is no way to detect if a user has written .gnu_attribute
> 4,0
> > nor is there a command-line/module option to assert it.
> 
> If no attributes or command-line options are given, we can use the
> default
> command-line setting for the configuration, which I think at the moment
> is always -mdouble-float.  This will give a specific ABI.
> 
> This is just like the way that a file assembled without any other
> information will get the default ISA level and CPU.

True but GCC will be passing these flags implicitly. Perhaps I'm attacking
this issue from the wrong side. Would you be OK with us no longer passing:

-msoft-float, -mfpxx, -mfp64, -msingle-float

from compiler driver to assembler and instead rely on the fact that the
compiler will have emitted a .module directive to indicate which of these
is active. For hand-crafted assembly code assembled via the GCC driver
it will get default -mfp32/double-float. 'Correct' multi-abi hand written
assembly code should be pre-processed and use the __mips_fpr and
__mips_soft_float etc macros to set a .module directive and correctly
enable the code for the selected ABI (or error if the module doesn't
support it).

> > Any thoughts on what to do with attribute 4,0? Since it is not very
> safe
> > in GCC terms then we could just not support it I suppose.
> 
> We need a way for code that doesn't use FP to say that it doesn't care
> about the FPU setting, so 4,0 seems fine to me.

I was trying to point out that if I were to remove the code that checks
if any FP code has been seen before inferring a hard-float ABI, then
there would be no way for a mode to end up marked as ABI_FP_ANY. The
patch as written will support 4,0 iff no floating point instruction has
been included in the text and not targeting soft-float.

> > I expect you can guess that I'm still not convinced by providing no
> safety net
> > at all for hand-written code :-) But I do understand the desire to
> keep things
> > clear cut. The original plan would have avoided this issue as it
> defined new
> > directives to get into fpxx. Perhaps it won't be an issue but I really
> wanted
> > to be able to provide distribution maintainers with a tool to detect
> which
> > objects remained FP32 even after the transition to an FPXX toolchain.
> 
> To be clear, I'm all in favour of warning about code that is wrong
> according
> to the current mips_opts.  My objections were about not trusting ".set"
> and about having complicated attempts to infer the ABI.
> 
> If the code is modern enough to use .gnu_attribute, we'll warn or error
> (maybe the latter is best) about it being incompatible with -mfpxx.
> If the code isn't modern enough to use .gnu_attribute and doesn't use
> .set fp=, we should warn about anything that is obviously incompatible
> with fp=xx (although clearly the warning can't be perfect).  In those
> two cases we have the safety net.

I can't think of any assembler level checks that can determine whether
FP code complies with the ABIs or not. 

> If the code doesn't use .gnu_attribute but does use .set fp=, we simply
> can't tell it apart from code is validly assuming a particular FPR size
> (after appropriate checking).

OK. Let's look at GCC specs as an alternative way to get some safety in
all this. I'll post the GCC patch today.

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-07 13:07                                                       ` Matthew Fortune
@ 2014-05-07 13:38                                                         ` Richard Sandiford
  2014-05-07 14:13                                                           ` Matthew Fortune
  0 siblings, 1 reply; 41+ messages in thread
From: Richard Sandiford @ 2014-05-07 13:38 UTC (permalink / raw)
  To: Matthew Fortune
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler

Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
>> >> > +      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");
>> >>
>> >> I don't think we should warn about code that's compatible with the
>> >> current ".set fp".  How would you write a warning-free fp=64 ifunc?
>> >
>> > This is perhaps a non-intuitive piece of code but the
>> mips_defer_fp_warn flag
>> > will only be set if the overall module was not FPXX, the current mode
>> is
>> > not FPXX and the two do not match. The only way to support having both
>> FP32
>> > and FP64 in the same module is if the overall module is FPXX. The
>> > warning handles
>> > the case of 'xx' when in reality it can never happen. It can only
>> print
>> > fp32,fp64
>> > or fp64,fp32. The reason the overall module must be FPXX (or matching
>> > the current
>> > mode) is because that is the only way you get the choice of mode at
>> > runtime (FPXX)
>> > or the correct mode to start with (FP64).
>> 
>> So the warning is about cases like ".module fp=32 .... .set fp=64 .... "
>> and ".module fp=64 .... .set fp=32 .... "?  I still think that if the
>> user
>> has code in a .set fp=NN block, we should assume that they really do
>> want
>> to write some code for NN-bit FPRs.
>
> The check is there because the code simply has no chance at all of getting
> the correct mode at runtime if the module mode is not FPXX.

Why not though?  What if you build an application that uses one FR setting
but has some interface code to allow it run plugins from the other?
That interface code would need to be written in asm and could legitimately
change the fp setting.

.set is there for that kind of thing, or other such local violations
of the ABI for reasons that tool writers can't predict in advance.

>> If we're going to treat this as a problem, I think we should do it when
>> the ".set" is first encountered.
>
> I'm fine with doing this instead, there is little value in allowing a
> .set fp=[32|64] when the region of code it encompasses does not really
> use any floating-point or uses a subset of floating-point.

It was an "if we're going to" in the sense of "I don't think we should". :-)

>> >> > @@ -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))
>> >>
>> >> We should keep the bit about forcing it to memory otherwise.
>> >> Can you explain the HAVE_32BIT_FPRS && mips_opts.fp != 0 thing a bit
>> more?
>> >> Maybe we should get rid of HAVE_32BIT_FPRS and HAVE_64BIT_FPRS now
>> that
>> >> there are three alternatives and just test mips_opts.fp directly.
>> >> (Please do that as a separate patch though -- there are quite a few
>> >> different things going on in the current patch.)
>> >
>> > The final clause is to allow 64-bit constant generation when the value
>> would
>> > need to be moved to FPRs using a pair of MTC1s (not MTHC1). I retained
>> the
>> > HAVE_32BIT_FPRS clause as it expands to more than just fp != 64, but I
>> don't
>> > understand why it is more complex than that. The fp != 0 could just as
>> easily
>> > have been fp == 32 which is probably clearer and is there to ensure
>> that
>> > we don't break the FPXX ABI by using an MTC1 for the high part of a
>> 64-bit
>> > value.
>> >
>> > There are still only two widths of register. The fact we are targeting
>> FPXX
>> > is only relevant in a small number of places, otherwise it is simply
>> 32-bit
>> > registers. So I would be changing HAVE_32BIT_FPRS to fp != 64 anyway.
>> 
>> That's fine.  I still think it's clearer, since it's fairly obvious that
>> fp != 64 means "not fp=64 mode", whereas it isn't as obvious that
>> HAVE_32BIT_FPRS means "fp=32 or fp=xx" mode.  Having to combine checks
>> for HAVE_NNBIT_FPRS and mips_opts.fp seems a bit weird.
>> 
>> I'd missed that HAVE_32BIT_FPRS depends on the ISA.  I'm not sure that's
>> a good idea either, but if we have to keep it, maybe a macro like
>> FPR_SIZE
>> would be better than direct checks for mips_opts.fp.
>
> I'd change the definition of HAVE_32BIT_FPRS if I was sure enough that it
> wouldn't break things but I can't tell.
>
> Some of this ugliness comes out of the fact that we have three sources
> of information about FP state: singlefloat, softfloat and fp. Merging
> these three together to give a single floating-point status may be a good
> idea but I don't really want to do that in this patch.
>
> Am I OK to just change this:
> +	  || (HAVE_32BIT_FPRS && mips_opts.fp != 0))
> To either:
> +	  || (HAVE_32BIT_FPRS && mips_opts.fp == 32))
> Or more boldly
> +	  || (mips_opts.fp == 32))

What I meant by the FPR_SIZE thing was something like:

#define FPR_SIZE \
  (mips_opts.fp == 64 && !ISA_HAS_64BIT_FPRS (mips_opts.isa) \
   ? 32 \
   : mips_opts.fp)

Then replace all HAVE_32BIT_FPRS checks with FPR_SIZE checks.
This particular case would use FPR_SIZE == 32.

>> >> > +@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.
>> >>
>> >> I'm not sure about the sizeof (double) thing: I think it's legitimate
>> >> to use -msingle-float with a 32-bit FPU and use software libraries
>> >> for double-precision.
>> >
>> > Granted. But I believe the intent of the existing single-precision ABI
>> is
>> > that it is also -fshort-double in GCC terms (r5900 I believe). We
>> would
>> > need to track both single-float and single-float short-double
>> separately.
>> > There are no current plans (from us at least) to support just
>> > single-precision with softfloat doubles.  The R6 spec re-introduces
>> the
>> > idea of a single precision only FPU and this is planned to be
>> primarily
>> > supported with short-doubles.
>> 
>> But emulated double-precision is definitely supported (and IIRC was also
>> used for r5900 GNU/Linux).  Maybe:
>> 
>>    Any double-precision operations must be emulated by software.
>> 
>> instead?
>
> OK. But we will need to introduce a further FP ABI for short-double as part
> of R6 (bare metal only) then. I haven't looked to see what the hybrid
> calling convention ends up as for single-float (without short-double) I
> wonder what happens to doubles.

This attribute is about the FPU rather than C types though.  I think any
-fshort-double annotation should be a separate flag.  There's nothing to
stop you using it with -msoft-float too.

>> >> > +@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.
>> >>
>> >> Obviously the "and instruction usage" is the contentious bit here :-)
>> >
>> > If we go with not having any safety net for hand written assembler
>> then
>> > instruction usage does still need to be included in this decision in
>> order
>> > to support getting modules that end up as FP 'ANY' i.e. no-float.
>> Otherwise
>> > we must write-off the ability to generate modules without a specific
>> FP ABI
>> > because there is no way to detect if a user has written .gnu_attribute
>> 4,0
>> > nor is there a command-line/module option to assert it.
>> 
>> If no attributes or command-line options are given, we can use the
>> default
>> command-line setting for the configuration, which I think at the moment
>> is always -mdouble-float.  This will give a specific ABI.
>> 
>> This is just like the way that a file assembled without any other
>> information will get the default ISA level and CPU.
>
> True but GCC will be passing these flags implicitly. Perhaps I'm attacking
> this issue from the wrong side. Would you be OK with us no longer passing:
>
> -msoft-float, -mfpxx, -mfp64, -msingle-float
>
> from compiler driver to assembler and instead rely on the fact that the
> compiler will have emitted a .module directive to indicate which of these
> is active.

No, the GCC driver should still direct the assembler and linker as well
as the compiler proper.

>> > Any thoughts on what to do with attribute 4,0? Since it is not very
>> safe
>> > in GCC terms then we could just not support it I suppose.
>> 
>> We need a way for code that doesn't use FP to say that it doesn't care
>> about the FPU setting, so 4,0 seems fine to me.
>
> I was trying to point out that if I were to remove the code that checks
> if any FP code has been seen before inferring a hard-float ABI, then
> there would be no way for a mode to end up marked as ABI_FP_ANY. The
> patch as written will support 4,0 iff no floating point instruction has
> been included in the text and not targeting soft-float.

Code explicitly marked .gnu_attribute 4,0 should end up as FP_ANY.

>> > I expect you can guess that I'm still not convinced by providing no
>> safety net
>> > at all for hand-written code :-) But I do understand the desire to
>> keep things
>> > clear cut. The original plan would have avoided this issue as it
>> defined new
>> > directives to get into fpxx. Perhaps it won't be an issue but I really
>> wanted
>> > to be able to provide distribution maintainers with a tool to detect
>> which
>> > objects remained FP32 even after the transition to an FPXX toolchain.
>> 
>> To be clear, I'm all in favour of warning about code that is wrong
>> according
>> to the current mips_opts.  My objections were about not trusting ".set"
>> and about having complicated attempts to infer the ABI.
>> 
>> If the code is modern enough to use .gnu_attribute, we'll warn or error
>> (maybe the latter is best) about it being incompatible with -mfpxx.
>> If the code isn't modern enough to use .gnu_attribute and doesn't use
>> .set fp=, we should warn about anything that is obviously incompatible
>> with fp=xx (although clearly the warning can't be perfect).  In those
>> two cases we have the safety net.
>
> I can't think of any assembler level checks that can determine whether
> FP code complies with the ABIs or not. 

Obviously it can't be perfect.  But what I meant was: you had code to
infer the ABI from the instructions.  That implies that some instructions
rule out some of the -mhard-float possibilities.  Rather than infer the ABI
from that information, we should turn it around and diagnose cases where
an instruction is inconsistent with the current mips_opts.  E.g. using
"add.d $f1, $f1, $f1" in fp=xx code would get a diagnostic.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 1/2] Add support for O32 FPXX ABI
  2014-05-07 13:38                                                         ` Richard Sandiford
@ 2014-05-07 14:13                                                           ` Matthew Fortune
  0 siblings, 0 replies; 41+ messages in thread
From: Matthew Fortune @ 2014-05-07 14:13 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: macro, Joseph Myers (joseph@codesourcery.com), binutils, Rich Fuhler



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: 07 May 2014 14:38
> To: Matthew Fortune
> Cc: macro@codesourcery.com; Joseph Myers (joseph@codesourcery.com);
> binutils@sourceware.org; Rich Fuhler
> Subject: Re: [PATCH 1/2] Add support for O32 FPXX ABI
> 
> Matthew Fortune <Matthew.Fortune@imgtec.com> writes:
> >> >> > +      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");
> >> >>
> >> >> I don't think we should warn about code that's compatible with the
> >> >> current ".set fp".  How would you write a warning-free fp=64
> ifunc?
> >> >
> >> > This is perhaps a non-intuitive piece of code but the
> >> mips_defer_fp_warn flag
> >> > will only be set if the overall module was not FPXX, the current
> mode
> >> is
> >> > not FPXX and the two do not match. The only way to support having
> both
> >> FP32
> >> > and FP64 in the same module is if the overall module is FPXX. The
> >> > warning handles
> >> > the case of 'xx' when in reality it can never happen. It can only
> >> print
> >> > fp32,fp64
> >> > or fp64,fp32. The reason the overall module must be FPXX (or
> matching
> >> > the current
> >> > mode) is because that is the only way you get the choice of mode at
> >> > runtime (FPXX)
> >> > or the correct mode to start with (FP64).
> >>
> >> So the warning is about cases like ".module fp=32 .... .set fp=64
> .... "
> >> and ".module fp=64 .... .set fp=32 .... "?  I still think that if the
> >> user
> >> has code in a .set fp=NN block, we should assume that they really do
> >> want
> >> to write some code for NN-bit FPRs.
> >
> > The check is there because the code simply has no chance at all of
> getting
> > the correct mode at runtime if the module mode is not FPXX.
> 
> Why not though?  What if you build an application that uses one FR
> setting
> but has some interface code to allow it run plugins from the other?
> That interface code would need to be written in asm and could
> legitimately
> change the fp setting.
> 
> .set is there for that kind of thing, or other such local violations
> of the ABI for reasons that tool writers can't predict in advance.

Agreed. Ignore previous comments. I'll remove the warning.

> >> If we're going to treat this as a problem, I think we should do it
> when
> >> the ".set" is first encountered.
> >
> > I'm fine with doing this instead, there is little value in allowing a
> > .set fp=[32|64] when the region of code it encompasses does not really
> > use any floating-point or uses a subset of floating-point.
> 
> It was an "if we're going to" in the sense of "I don't think we should".
> :-)
> 
> >> >> > @@ -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))
> >> >>
> >> >> We should keep the bit about forcing it to memory otherwise.
> >> >> Can you explain the HAVE_32BIT_FPRS && mips_opts.fp != 0 thing a
> bit
> >> more?
> >> >> Maybe we should get rid of HAVE_32BIT_FPRS and HAVE_64BIT_FPRS now
> >> that
> >> >> there are three alternatives and just test mips_opts.fp directly.
> >> >> (Please do that as a separate patch though -- there are quite a
> few
> >> >> different things going on in the current patch.)
> >> >
> >> > The final clause is to allow 64-bit constant generation when the
> value
> >> would
> >> > need to be moved to FPRs using a pair of MTC1s (not MTHC1). I
> retained
> >> the
> >> > HAVE_32BIT_FPRS clause as it expands to more than just fp != 64,
> but I
> >> don't
> >> > understand why it is more complex than that. The fp != 0 could just
> as
> >> easily
> >> > have been fp == 32 which is probably clearer and is there to ensure
> >> that
> >> > we don't break the FPXX ABI by using an MTC1 for the high part of a
> >> 64-bit
> >> > value.
> >> >
> >> > There are still only two widths of register. The fact we are
> targeting
> >> FPXX
> >> > is only relevant in a small number of places, otherwise it is
> simply
> >> 32-bit
> >> > registers. So I would be changing HAVE_32BIT_FPRS to fp != 64
> anyway.
> >>
> >> That's fine.  I still think it's clearer, since it's fairly obvious
> that
> >> fp != 64 means "not fp=64 mode", whereas it isn't as obvious that
> >> HAVE_32BIT_FPRS means "fp=32 or fp=xx" mode.  Having to combine
> checks
> >> for HAVE_NNBIT_FPRS and mips_opts.fp seems a bit weird.
> >>
> >> I'd missed that HAVE_32BIT_FPRS depends on the ISA.  I'm not sure
> that's
> >> a good idea either, but if we have to keep it, maybe a macro like
> >> FPR_SIZE
> >> would be better than direct checks for mips_opts.fp.
> >
> > I'd change the definition of HAVE_32BIT_FPRS if I was sure enough that
> it
> > wouldn't break things but I can't tell.
> >
> > Some of this ugliness comes out of the fact that we have three sources
> > of information about FP state: singlefloat, softfloat and fp. Merging
> > these three together to give a single floating-point status may be a
> good
> > idea but I don't really want to do that in this patch.
> >
> > Am I OK to just change this:
> > +	  || (HAVE_32BIT_FPRS && mips_opts.fp != 0))
> > To either:
> > +	  || (HAVE_32BIT_FPRS && mips_opts.fp == 32))
> > Or more boldly
> > +	  || (mips_opts.fp == 32))
> 
> What I meant by the FPR_SIZE thing was something like:
> 
> #define FPR_SIZE \
>   (mips_opts.fp == 64 && !ISA_HAS_64BIT_FPRS (mips_opts.isa) \
>    ? 32 \
>    : mips_opts.fp)
> 
> Then replace all HAVE_32BIT_FPRS checks with FPR_SIZE checks.
> This particular case would use FPR_SIZE == 32.
> 
> >> >> > +@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.
> >> >>
> >> >> I'm not sure about the sizeof (double) thing: I think it's
> legitimate
> >> >> to use -msingle-float with a 32-bit FPU and use software libraries
> >> >> for double-precision.
> >> >
> >> > Granted. But I believe the intent of the existing single-precision
> ABI
> >> is
> >> > that it is also -fshort-double in GCC terms (r5900 I believe). We
> >> would
> >> > need to track both single-float and single-float short-double
> >> separately.
> >> > There are no current plans (from us at least) to support just
> >> > single-precision with softfloat doubles.  The R6 spec re-introduces
> >> the
> >> > idea of a single precision only FPU and this is planned to be
> >> primarily
> >> > supported with short-doubles.
> >>
> >> But emulated double-precision is definitely supported (and IIRC was
> also
> >> used for r5900 GNU/Linux).  Maybe:
> >>
> >>    Any double-precision operations must be emulated by software.
> >>
> >> instead?
> >
> > OK. But we will need to introduce a further FP ABI for short-double as
> part
> > of R6 (bare metal only) then. I haven't looked to see what the hybrid
> > calling convention ends up as for single-float (without short-double)
> I
> > wonder what happens to doubles.
> 
> This attribute is about the FPU rather than C types though.  I think any
> -fshort-double annotation should be a separate flag.  There's nothing to
> stop you using it with -msoft-float too.

True, and have that extra flag take part in the compatibility checks.

> >> >> > +@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.
> >> >>
> >> >> Obviously the "and instruction usage" is the contentious bit here
> :-)
> >> >
> >> > If we go with not having any safety net for hand written assembler
> >> then
> >> > instruction usage does still need to be included in this decision
> in
> >> order
> >> > to support getting modules that end up as FP 'ANY' i.e. no-float.
> >> Otherwise
> >> > we must write-off the ability to generate modules without a
> specific
> >> FP ABI
> >> > because there is no way to detect if a user has written
> .gnu_attribute
> >> 4,0
> >> > nor is there a command-line/module option to assert it.
> >>
> >> If no attributes or command-line options are given, we can use the
> >> default
> >> command-line setting for the configuration, which I think at the
> moment
> >> is always -mdouble-float.  This will give a specific ABI.
> >>
> >> This is just like the way that a file assembled without any other
> >> information will get the default ISA level and CPU.
> >
> > True but GCC will be passing these flags implicitly. Perhaps I'm
> attacking
> > this issue from the wrong side. Would you be OK with us no longer
> passing:
> >
> > -msoft-float, -mfpxx, -mfp64, -msingle-float
> >
> > from compiler driver to assembler and instead rely on the fact that
> the
> > compiler will have emitted a .module directive to indicate which of
> these
> > is active.
> 
> No, the GCC driver should still direct the assembler and linker as well
> as the compiler proper.

I'll drop this topic for now but I expect to have to reopen it.

> >> > Any thoughts on what to do with attribute 4,0? Since it is not very
> >> safe
> >> > in GCC terms then we could just not support it I suppose.
> >>
> >> We need a way for code that doesn't use FP to say that it doesn't
> care
> >> about the FPU setting, so 4,0 seems fine to me.
> >
> > I was trying to point out that if I were to remove the code that
> checks
> > if any FP code has been seen before inferring a hard-float ABI, then
> > there would be no way for a mode to end up marked as ABI_FP_ANY. The
> > patch as written will support 4,0 iff no floating point instruction
> has
> > been included in the text and not targeting soft-float.
> 
> Code explicitly marked .gnu_attribute 4,0 should end up as FP_ANY.

So I should move the setting of the gnu_attribute based on command-line
module options into the file level option checking code so that it can
be overridden by the user later. Otherwise, I won't be able to tell the
difference between the default (4,0) and an explicit (4,0).

Regards,
Matthew

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2014-05-07 14:13 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-04 14:26 [PATCH 1/2] Add support for O32 FPXX ABI Matthew Fortune
2014-04-05  8:12 ` Richard Sandiford
2014-04-07  9:58   ` Matthew Fortune
2014-04-07 10:53     ` Richard Sandiford
2014-04-07 12:20       ` Matthew Fortune
2014-04-07 13:21         ` Richard Sandiford
2014-04-10  6:50           ` Matthew Fortune
2014-04-10  8:31             ` Richard Sandiford
2014-04-10 11:44               ` Matthew Fortune
2014-04-10 12:49                 ` Richard Sandiford
2014-04-11  7:14                   ` Matthew Fortune
2014-04-11  8:26                     ` Richard Sandiford
2014-04-11 13:14                       ` Matthew Fortune
2014-04-11 16:29                         ` Richard Sandiford
2014-04-11 18:27                           ` Matthew Fortune
2014-04-11 18:31                             ` Richard Sandiford
2014-04-15 12:28                               ` Matthew Fortune
2014-04-15 20:21                                 ` Richard Sandiford
2014-04-15 21:09                                   ` Matthew Fortune
2014-04-16  8:25                                     ` Richard Sandiford
2014-04-24 16:11                                       ` Matthew Fortune
2014-04-24 17:18                                         ` Richard Sandiford
2014-04-24 18:39                                           ` Matthew Fortune
2014-04-24 20:21                                             ` Maciej W. Rozycki
2014-04-25  7:39                                             ` Richard Sandiford
2014-05-01 14:43                                               ` Matthew Fortune
2014-05-02 20:31                                                 ` Doug Gilmore
2014-05-06 11:57                                                   ` Matthew Fortune
2014-05-06 12:40                                                     ` Richard Sandiford
2014-05-06 14:08                                                       ` Matthew Fortune
2014-05-06 14:39                                                         ` Richard Sandiford
2014-05-06 19:19                                                 ` Richard Sandiford
2014-05-06 22:47                                                   ` Maciej W. Rozycki
2014-05-06 22:51                                                   ` Matthew Fortune
2014-05-07  8:01                                                     ` Richard Sandiford
2014-05-07 13:07                                                       ` Matthew Fortune
2014-05-07 13:38                                                         ` Richard Sandiford
2014-05-07 14:13                                                           ` Matthew Fortune
2014-04-23 20:43                           ` Joseph S. Myers
2014-04-22 17:05         ` Joseph S. Myers
2014-04-23  7:36           ` Matthew Fortune

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).