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


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

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


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


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