]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-sparc.c
2.41 Release sources
[thirdparty/binutils-gdb.git] / gas / config / tc-sparc.c
index 1d2f945d6797d4131656cad958013385f40af061..eada5e5a0dad855d38a6c00384c906920fa223f6 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-sparc.c -- Assemble for the SPARC
-   Copyright (C) 1989-2014 Free Software Foundation, Inc.
+   Copyright (C) 1989-2023 Free Software Foundation, Inc.
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
 #include "opcode/sparc.h"
 #include "dw2gencfi.h"
 
-#ifdef OBJ_ELF
 #include "elf/sparc.h"
 #include "dwarf2dbg.h"
-#endif
 
 /* Some ancient Sun C compilers would not take such hex constants as
    unsigned, and would end up sign-extending them to form an offsetT,
@@ -36,6 +34,7 @@
 #define U0x80000000 ((((unsigned long) 1 << 16) << 15))
 
 static int sparc_ip (char *, const struct sparc_opcode **);
+static int parse_sparc_asi (char **, const sparc_asi **);
 static int parse_keyword_arg (int (*) (const char *), char **, int *);
 static int parse_const_expr_arg (char **, int *);
 static int get_expression (char *);
@@ -47,7 +46,7 @@ static int get_expression (char *);
 #ifndef DEFAULT_ARCH
 #define DEFAULT_ARCH "sparclite"
 #endif
-static char *default_arch = DEFAULT_ARCH;
+static const char *default_arch = DEFAULT_ARCH;
 
 /* Non-zero if the initial values of `max_architecture' and `sparc_arch_size'
    have been set.  */
@@ -69,7 +68,6 @@ static int sparc_arch_size;
    changes the value before md_show_usage is called.  */
 static int default_arch_size;
 
-#ifdef OBJ_ELF
 /* The currently selected v9 memory model.  Currently only used for
    ELF.  */
 static enum { MM_TSO, MM_PSO, MM_RMO } sparc_memory_model = MM_RMO;
@@ -77,11 +75,10 @@ static enum { MM_TSO, MM_PSO, MM_RMO } sparc_memory_model = MM_RMO;
 #ifndef TE_SOLARIS
 /* Bitmask of instruction types seen so far, used to populate the
    GNU attributes section with hwcap information.  */
-static bfd_uint64_t hwcap_seen;
-#endif
+static uint64_t hwcap_seen;
 #endif
 
-static bfd_uint64_t hwcap_allowed;
+static uint64_t hwcap_allowed;
 
 static int architecture_requested;
 static int warn_on_bump;
@@ -90,10 +87,15 @@ static int warn_on_bump;
    architecture, issue a warning.  */
 static enum sparc_opcode_arch_val warn_after_architecture;
 
-/* Non-zero if as should generate error if an undeclared g[23] register
-   has been used in -64.  */
+/* Non-zero if the assembler should generate error if an undeclared
+   g[23] register has been used in -64.  */
 static int no_undeclared_regs;
 
+/* Non-zero if the assembler should generate a warning if an
+   unpredictable DCTI (delayed control transfer instruction) couple is
+   found.  */
+static int dcti_couples_detect;
+
 /* Non-zero if we should try to relax jumps and calls.  */
 static int sparc_relax;
 
@@ -124,7 +126,7 @@ int sparc_cie_data_alignment;
 #endif
 
 /* Handle of the OPCODE hash table.  */
-static struct hash_control *op_hash;
+static htab_t op_hash;
 
 static void s_data1 (void);
 static void s_seg (int);
@@ -134,9 +136,7 @@ static void s_common (int);
 static void s_empty (int);
 static void s_uacons (int);
 static void s_ncons (int);
-#ifdef OBJ_ELF
 static void s_register (int);
-#endif
 
 const pseudo_typeS md_pseudo_table[] =
 {
@@ -156,13 +156,11 @@ const pseudo_typeS md_pseudo_table[] =
   {"uahalf", s_uacons, 2},
   {"uaword", s_uacons, 4},
   {"uaxword", s_uacons, 8},
-#ifdef OBJ_ELF
   /* These are specific to sparc/svr4.  */
   {"2byte", s_uacons, 2},
   {"4byte", s_uacons, 4},
   {"8byte", s_uacons, 8},
   {"register", s_register, 0},
-#endif
   {NULL, 0, 0},
 };
 
@@ -200,7 +198,7 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 struct sparc_it
   {
-    char *error;
+    const char *error;
     unsigned long opcode;
     struct nlist *nlistp;
     expressionS exp;
@@ -221,78 +219,65 @@ static void output_insn (const struct sparc_opcode *, struct sparc_it *);
 enum sparc_arch_types {v6, v7, v8, leon, sparclet, sparclite, sparc86x, v8plus,
                       v8plusa, v9, v9a, v9b, v9_64};
 
-/* Hardware capability sets, used to keep sparc_arch_table easy to
-   read.  */
-#define HWS_V8 HWCAP_MUL32 | HWCAP_DIV32 | HWCAP_FSMULD
-#define HWS_V9 HWS_V8 | HWCAP_POPC
-#define HWS_VA HWS_V9 | HWCAP_VIS
-#define HWS_VB HWS_VA | HWCAP_VIS2
-#define HWS_VC HWS_VB | HWCAP_ASI_BLK_INIT
-#define HWS_VD HWS_VC | HWCAP_FMAF | HWCAP_VIS3 | HWCAP_HPC
-#define HWS_VE HWS_VD                                                   \
-  | HWCAP_AES | HWCAP_DES | HWCAP_KASUMI | HWCAP_CAMELLIA               \
-  | HWCAP_MD5 | HWCAP_SHA1 | HWCAP_SHA256 |HWCAP_SHA512 | HWCAP_MPMUL   \
-  | HWCAP_MONT | HWCAP_CRC32C | HWCAP_CBCOND | HWCAP_PAUSE
-#define HWS_VV HWS_VE | HWCAP_FJFMAU | HWCAP_IMA
-#define HWS_VM HWS_VV
-
-#define HWS2_VM                                                        \
-  HWCAP2_VIS3B | HWCAP2_ADP | HWCAP2_SPARC5 | HWCAP2_MWAIT     \
-  | HWCAP2_XMPMUL | HWCAP2_XMONT
-
 static struct sparc_arch {
-  char *name;
-  char *opcode_arch;
+  const char *name;
+  const char *opcode_arch;
   enum sparc_arch_types arch_type;
   /* Default word size, as specified during configuration.
      A value of zero means can't be used to specify default architecture.  */
   int default_arch_size;
   /* Allowable arg to -A?  */
   int user_option_p;
+  /* Extra hardware capabilities allowed.  These are added to the
+     hardware capabilities associated with the opcode
+     architecture.  */
   int hwcap_allowed;
   int hwcap2_allowed;
 } sparc_arch_table[] = {
   { "v6",         "v6",  v6,  0, 1, 0, 0 },
   { "v7",         "v7",  v7,  0, 1, 0, 0 },
-  { "v8",         "v8",  v8, 32, 1, HWS_V8, 0 },
-  { "v8a",        "v8",  v8, 32, 1, HWS_V8, 0 },
-  { "sparc",      "v9",  v9,  0, 1, HWCAP_V8PLUS|HWS_V9, 0 },
-  { "sparcvis",   "v9a", v9,  0, 1, HWS_VA, 0 },
-  { "sparcvis2",  "v9b", v9,  0, 1, HWS_VB, 0 },
-  { "sparcfmaf",  "v9b", v9,  0, 1, HWS_VB|HWCAP_FMAF, 0 },
-  { "sparcima",   "v9b", v9,  0, 1, HWS_VB|HWCAP_FMAF|HWCAP_IMA, 0 },
-  { "sparcvis3",  "v9b", v9,  0, 1, HWS_VB|HWCAP_FMAF|HWCAP_VIS3|HWCAP_HPC, 0 },
-  { "sparcvis3r", "v9b", v9,  0, 1, HWS_VB|HWCAP_FMAF|HWCAP_VIS3|HWCAP_HPC|HWCAP_FJFMAU, 0 },
-
-  { "sparc4",     "v9b", v9,  0, 1, HWS_VV, 0 },
-  { "sparc5",     "v9b", v9,  0, 1, HWS_VM, HWS2_VM },
-
-  { "leon",      "leon",      leon,      32, 1, HWS_V8, 0 },
-  { "sparclet",  "sparclet",  sparclet,  32, 1, HWS_V8, 0 },
-  { "sparclite", "sparclite", sparclite, 32, 1, HWS_V8, 0 },
-  { "sparc86x",  "sparclite", sparc86x,  32, 1, HWS_V8, 0 },
-
-  { "v8plus",  "v9",  v9,  0, 1, HWCAP_V8PLUS|HWS_V9, 0 },
-  { "v8plusa", "v9a", v9,  0, 1, HWCAP_V8PLUS|HWS_VA, 0 },
-  { "v8plusb", "v9b", v9,  0, 1, HWCAP_V8PLUS|HWS_VB, 0 },
-  { "v8plusc", "v9b", v9,  0, 1, HWCAP_V8PLUS|HWS_VC, 0 },
-  { "v8plusd", "v9b", v9,  0, 1, HWCAP_V8PLUS|HWS_VD, 0 },
-  { "v8pluse", "v9b", v9,  0, 1, HWCAP_V8PLUS|HWS_VE, 0 },
-  { "v8plusv", "v9b", v9,  0, 1, HWCAP_V8PLUS|HWS_VV, 0 },
-  { "v8plusm", "v9b", v9,  0, 1, HWCAP_V8PLUS|HWS_VM, 0 },
+  { "v8",         "v8",  v8, 32, 1, 0, 0 },
+  { "v8a",        "v8",  v8, 32, 1, 0, 0 },
+  { "sparc",      "v9",  v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "sparcvis",   "v9a", v9,  0, 1, 0, 0 },
+  { "sparcvis2",  "v9b", v9,  0, 1, 0, 0 },
+  { "sparcfmaf",  "v9b", v9,  0, 1, HWCAP_FMAF, 0 },
+  { "sparcima",   "v9b", v9,  0, 1, HWCAP_FMAF|HWCAP_IMA, 0 },
+  { "sparcvis3",  "v9b", v9,  0, 1, HWCAP_FMAF|HWCAP_VIS3|HWCAP_HPC, 0 },
+  { "sparcvis3r", "v9b", v9,  0, 1, HWCAP_FMAF|HWCAP_VIS3|HWCAP_HPC|HWCAP_FJFMAU, 0 },
+
+  { "sparc4",     "v9v", v9,  0, 1, 0, 0 },
+  { "sparc5",     "v9m", v9,  0, 1, 0, 0 },
+  { "sparc6",     "m8",  v9,  0, 1, 0, 0 },
+
+  { "leon",      "leon",      leon,      32, 1, 0, 0 },
+  { "sparclet",  "sparclet",  sparclet,  32, 1, 0, 0 },
+  { "sparclite", "sparclite", sparclite, 32, 1, 0, 0 },
+  { "sparc86x",  "sparclite", sparc86x,  32, 1, 0, 0 },
+
+  { "v8plus",  "v9",  v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "v8plusa", "v9a", v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "v8plusb", "v9b", v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "v8plusc", "v9c", v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "v8plusd", "v9d", v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "v8pluse", "v9e", v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "v8plusv", "v9v", v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "v8plusm", "v9m", v9,  0, 1, HWCAP_V8PLUS, 0 },
+  { "v8plusm8", "m8", v9,  0, 1, HWCAP_V8PLUS, 0 },
   
-  { "v9",      "v9",  v9,  0, 1, HWS_V9, 0 },
-  { "v9a",     "v9a", v9,  0, 1, HWS_VA, 0 },
-  { "v9b",     "v9b", v9,  0, 1, HWS_VB, 0 },
-  { "v9c",     "v9b", v9,  0, 1, HWS_VC, 0 },
-  { "v9d",     "v9b", v9,  0, 1, HWS_VD, 0 },
-  { "v9e",     "v9b", v9,  0, 1, HWS_VE, 0 },
-  { "v9v",     "v9b", v9,  0, 1, HWS_VV, 0 },
-  { "v9m",     "v9b", v9,  0, 1, HWS_VM, HWS2_VM },
+  { "v9",      "v9",  v9,  0, 1, 0, 0 },
+  { "v9a",     "v9a", v9,  0, 1, 0, 0 },
+  { "v9b",     "v9b", v9,  0, 1, 0, 0 },
+  { "v9c",     "v9c", v9,  0, 1, 0, 0 },
+  { "v9d",     "v9d", v9,  0, 1, 0, 0 },
+  { "v9e",     "v9e", v9,  0, 1, 0, 0 },
+  { "v9v",     "v9v", v9,  0, 1, 0, 0 },
+  { "v9m",     "v9m", v9,  0, 1, 0, 0 },
+  { "v9m8",     "m8", v9,  0, 1, 0, 0 },
 
   /* This exists to allow configure.tgt to pass one
      value to specify both the default machine and default word size.  */
-  { "v9-64",   "v9",  v9, 64, 0, HWS_V9, 0 },
+  { "v9-64",   "v9",  v9, 64, 0, 0, 0 },
   { NULL, NULL, v8, 0, 0, 0, 0 }
 };
 
@@ -300,7 +285,7 @@ static struct sparc_arch {
 static enum sparc_arch_types default_arch_type;
 
 static struct sparc_arch *
-lookup_arch (char *name)
+lookup_arch (const char *name)
 {
   struct sparc_arch *sa;
 
@@ -332,54 +317,34 @@ init_default_arch (void)
   default_arch_type = sa->arch_type;
 }
 
-/* Called by TARGET_FORMAT.  */
+/* Called by TARGET_MACH.  */
 
-const char *
-sparc_target_format (void)
+unsigned long
+sparc_mach (void)
 {
   /* We don't get a chance to initialize anything before we're called,
      so handle that now.  */
   if (! default_init_p)
     init_default_arch ();
 
-#ifdef OBJ_AOUT
-#ifdef TE_NetBSD
-  return "a.out-sparc-netbsd";
-#else
-#ifdef TE_SPARCAOUT
-  if (target_big_endian)
-    return "a.out-sunos-big";
-  else if (default_arch_type == sparc86x && target_little_endian_data)
-    return "a.out-sunos-big";
-  else
-    return "a.out-sparc-little";
-#else
-  return "a.out-sunos-big";
-#endif
-#endif
-#endif
+  return sparc_arch_size == 64 ? bfd_mach_sparc_v9 : bfd_mach_sparc;
+}
 
-#ifdef OBJ_BOUT
-  return "b.out.big";
-#endif
+/* Called by TARGET_FORMAT.  */
 
-#ifdef OBJ_COFF
-#ifdef TE_LYNX
-  return "coff-sparc-lynx";
-#else
-  return "coff-sparc";
-#endif
-#endif
+const char *
+sparc_target_format (void)
+{
+  /* We don't get a chance to initialize anything before we're called,
+     so handle that now.  */
+  if (! default_init_p)
+    init_default_arch ();
 
 #ifdef TE_VXWORKS
   return "elf32-sparc-vxworks";
 #endif
 
-#ifdef OBJ_ELF
   return sparc_arch_size == 64 ? ELF64_TARGET_FORMAT : ELF_TARGET_FORMAT;
-#endif
-
-  abort ();
 }
 \f
 /* md_parse_option
@@ -436,15 +401,7 @@ sparc_target_format (void)
  *             error.  For example, from sparclite to v9.
  */
 
-#ifdef OBJ_ELF
 const char *md_shortopts = "A:K:VQ:sq";
-#else
-#ifdef OBJ_AOUT
-const char *md_shortopts = "A:k";
-#else
-const char *md_shortopts = "A:";
-#endif
-#endif
 struct option md_longopts[] = {
 #define OPTION_BUMP (OPTION_MD_BASE)
   {"bump", no_argument, NULL, OPTION_BUMP},
@@ -452,7 +409,6 @@ struct option md_longopts[] = {
   {"sparc", no_argument, NULL, OPTION_SPARC},
 #define OPTION_XARCH (OPTION_MD_BASE + 2)
   {"xarch", required_argument, NULL, OPTION_XARCH},
-#ifdef OBJ_ELF
 #define OPTION_32 (OPTION_MD_BASE + 3)
   {"32", no_argument, NULL, OPTION_32},
 #define OPTION_64 (OPTION_MD_BASE + 4)
@@ -463,7 +419,6 @@ struct option md_longopts[] = {
   {"PSO", no_argument, NULL, OPTION_PSO},
 #define OPTION_RMO (OPTION_MD_BASE + 7)
   {"RMO", no_argument, NULL, OPTION_RMO},
-#endif
 #ifdef SPARC_BIENDIAN
 #define OPTION_LITTLE_ENDIAN (OPTION_MD_BASE + 8)
   {"EL", no_argument, NULL, OPTION_LITTLE_ENDIAN},
@@ -474,23 +429,23 @@ struct option md_longopts[] = {
   {"enforce-aligned-data", no_argument, NULL, OPTION_ENFORCE_ALIGNED_DATA},
 #define OPTION_LITTLE_ENDIAN_DATA (OPTION_MD_BASE + 11)
   {"little-endian-data", no_argument, NULL, OPTION_LITTLE_ENDIAN_DATA},
-#ifdef OBJ_ELF
 #define OPTION_NO_UNDECLARED_REGS (OPTION_MD_BASE + 12)
   {"no-undeclared-regs", no_argument, NULL, OPTION_NO_UNDECLARED_REGS},
 #define OPTION_UNDECLARED_REGS (OPTION_MD_BASE + 13)
   {"undeclared-regs", no_argument, NULL, OPTION_UNDECLARED_REGS},
-#endif
 #define OPTION_RELAX (OPTION_MD_BASE + 14)
   {"relax", no_argument, NULL, OPTION_RELAX},
 #define OPTION_NO_RELAX (OPTION_MD_BASE + 15)
   {"no-relax", no_argument, NULL, OPTION_NO_RELAX},
+#define OPTION_DCTI_COUPLES_DETECT (OPTION_MD_BASE + 16)
+  {"dcti-couples-detect", no_argument, NULL, OPTION_DCTI_COUPLES_DETECT},
   {NULL, no_argument, NULL, 0}
 };
 
 size_t md_longopts_size = sizeof (md_longopts);
 
 int
-md_parse_option (int c, char *arg)
+md_parse_option (int c, const char *arg)
 {
   /* We don't get a chance to initialize anything before we're called,
      so handle that now.  */
@@ -505,20 +460,18 @@ md_parse_option (int c, char *arg)
       break;
 
     case OPTION_XARCH:
-#ifdef OBJ_ELF
-      if (!strncmp (arg, "v9", 2))
+      if (startswith (arg, "v9"))
        md_parse_option (OPTION_64, NULL);
       else
        {
-         if (!strncmp (arg, "v8", 2)
-             || !strncmp (arg, "v7", 2)
-             || !strncmp (arg, "v6", 2)
+         if (startswith (arg, "v8")
+             || startswith (arg, "v7")
+             || startswith (arg, "v6")
              || !strcmp (arg, "sparclet")
              || !strcmp (arg, "sparclite")
              || !strcmp (arg, "sparc86x"))
            md_parse_option (OPTION_32, NULL);
        }
-#endif
       /* Fall through.  */
 
     case 'A':
@@ -544,8 +497,16 @@ md_parse_option (int c, char *arg)
        if (!architecture_requested
            || opcode_arch > max_architecture)
          max_architecture = opcode_arch;
+
+       /* The allowed hardware capabilities are the implied by the
+          opcodes arch plus any extra capabilities defined in the GAS
+          arch.  */
        hwcap_allowed
-          |= (((bfd_uint64_t) sa->hwcap2_allowed) << 32) | sa->hwcap_allowed;
+         = (hwcap_allowed
+            | ((uint64_t) sparc_opcode_archs[opcode_arch].hwcaps2 << 32)
+            | ((uint64_t) sa->hwcap2_allowed << 32)
+            | sparc_opcode_archs[opcode_arch].hwcaps
+            | sa->hwcap_allowed);
        architecture_requested = 1;
       }
       break;
@@ -576,13 +537,6 @@ md_parse_option (int c, char *arg)
       break;
 #endif
 
-#ifdef OBJ_AOUT
-    case 'k':
-      sparc_pic_code = 1;
-      break;
-#endif
-
-#ifdef OBJ_ELF
     case OPTION_32:
     case OPTION_64:
       {
@@ -594,12 +548,12 @@ md_parse_option (int c, char *arg)
          {
            if (sparc_arch_size == 32)
              {
-               if (CONST_STRNEQ (*l, "elf32-sparc"))
+               if (startswith (*l, "elf32-sparc"))
                  break;
              }
            else
              {
-               if (CONST_STRNEQ (*l, "elf64-sparc"))
+               if (startswith (*l, "elf64-sparc"))
                  break;
              }
          }
@@ -657,7 +611,6 @@ md_parse_option (int c, char *arg)
     case OPTION_UNDECLARED_REGS:
       no_undeclared_regs = 0;
       break;
-#endif
 
     case OPTION_RELAX:
       sparc_relax = 1;
@@ -667,6 +620,10 @@ md_parse_option (int c, char *arg)
       sparc_relax = 0;
       break;
 
+    case OPTION_DCTI_COUPLES_DETECT:
+      dcti_couples_detect = 1;
+      break;
+
     default:
       return 0;
     }
@@ -721,11 +678,6 @@ md_show_usage (FILE *stream)
 --enforce-aligned-data force .long, etc., to be aligned correctly\n\
 -relax                 relax jumps and branches (default)\n\
 -no-relax              avoid changing any jumps and branches\n"));
-#ifdef OBJ_AOUT
-  fprintf (stream, _("\
--k                     generate PIC\n"));
-#endif
-#ifdef OBJ_ELF
   fprintf (stream, _("\
 -32                    create 32 bit object file\n\
 -64                    create 64 bit object file\n"));
@@ -744,10 +696,10 @@ md_show_usage (FILE *stream)
                        appropriate .register directive (default)\n\
 -no-undeclared-regs    force error on application global register usage\n\
                        without appropriate .register directive\n\
+--dcti-couples-detect  warn when an unpredictable DCTI couple is found\n\
 -q                     ignored\n\
 -Qy, -Qn               ignored\n\
 -s                     ignored\n"));
-#endif
 #ifdef SPARC_BIENDIAN
   fprintf (stream, _("\
 -EL                    generate code for a little endian machine\n\
@@ -758,11 +710,11 @@ md_show_usage (FILE *stream)
 }
 \f
 /* Native operand size opcode translation.  */
-struct
+static struct
   {
-    char *name;
-    char *name32;
-    char *name64;
+    const char *name;
+    const char *name32;
+    const char *name64;
   } native_op_table[] =
 {
   {"ldn", "ld", "ldx"},
@@ -782,7 +734,7 @@ struct
 
 struct priv_reg_entry
 {
-  char *name;
+  const char *name;
   int regnum;
 };
 
@@ -805,8 +757,9 @@ struct priv_reg_entry priv_reg_table[] =
   {"wstate", 14},
   {"fq", 15},
   {"gl", 16},
+  {"pmcdper", 23},
   {"ver", 31},
-  {"", -1},                    /* End marker.  */
+  {NULL, -1},                  /* End marker.  */
 };
 
 struct priv_reg_entry hpriv_reg_table[] =
@@ -816,14 +769,16 @@ struct priv_reg_entry hpriv_reg_table[] =
   {"hintp", 3},
   {"htba", 5},
   {"hver", 6},
+  {"hmcdper", 23},
+  {"hmcddfr", 24},
+  {"hva_mask_nz", 27},
   {"hstick_offset", 28},
   {"hstick_enable", 29},
   {"hstick_cmpr", 31},
-  {"", -1},                    /* End marker.  */
+  {NULL, -1},                  /* End marker.  */
 };
 
-/* v9a specific asrs.  This table is ordered by initial
-   letter, in reverse.  */
+/* v9a or later specific ancillary state registers. */
 
 struct priv_reg_entry v9a_asr_table[] =
 {
@@ -844,7 +799,7 @@ struct priv_reg_entry v9a_asr_table[] =
   {"dcr", 18},
   {"cfr", 26},
   {"clear_softint", 21},
-  {"", -1},                    /* End marker.  */
+  {NULL, -1},                  /* End marker.  */
 };
 
 static int
@@ -853,7 +808,130 @@ cmp_reg_entry (const void *parg, const void *qarg)
   const struct priv_reg_entry *p = (const struct priv_reg_entry *) parg;
   const struct priv_reg_entry *q = (const struct priv_reg_entry *) qarg;
 
-  return strcmp (q->name, p->name);
+  if (p->name == q->name)
+    return 0;
+  else if (p->name == NULL)
+    return 1;
+  else if (q->name == NULL)
+    return -1;
+  else
+    return strcmp (q->name, p->name);
+}
+\f
+/* sparc %-pseudo-operations.  */
+
+
+#define F_POP_V9       0x1 /* The pseudo-op is for v9 only.  */
+#define F_POP_PCREL    0x2 /* The pseudo-op can be used in pc-relative
+                              contexts.  */
+#define F_POP_TLS_CALL 0x4 /* The pseudo-op marks a tls call.  */
+#define F_POP_POSTFIX  0x8 /* The pseudo-op should appear after the
+                              last operand of an
+                              instruction. (Generally they can appear
+                              anywhere an immediate operand is
+                              expected.  */
+struct pop_entry
+{
+  /* The name as it appears in assembler.  */
+  const char *name;
+  /* The reloc this pseudo-op translates to.  */
+  bfd_reloc_code_real_type reloc;
+  /* Flags.  See F_POP_* above.  */
+  int flags;
+};
+
+struct pop_entry pop_table[] =
+{
+  { "hix",             BFD_RELOC_SPARC_HIX22,          F_POP_V9 },
+  { "lox",             BFD_RELOC_SPARC_LOX10,          F_POP_V9 },
+  { "hi",              BFD_RELOC_HI22,                 F_POP_PCREL },
+  { "lo",              BFD_RELOC_LO10,                 F_POP_PCREL },
+  { "pc22",            BFD_RELOC_SPARC_PC22,           F_POP_PCREL },
+  { "pc10",            BFD_RELOC_SPARC_PC10,           F_POP_PCREL },
+  { "hh",              BFD_RELOC_SPARC_HH22,           F_POP_V9|F_POP_PCREL },
+  { "hm",              BFD_RELOC_SPARC_HM10,           F_POP_V9|F_POP_PCREL },
+  { "lm",              BFD_RELOC_SPARC_LM22,           F_POP_V9|F_POP_PCREL },
+  { "h34",             BFD_RELOC_SPARC_H34,            F_POP_V9 },
+  { "l34",             BFD_RELOC_SPARC_L44,            F_POP_V9 },
+  { "h44",             BFD_RELOC_SPARC_H44,            F_POP_V9 },
+  { "m44",             BFD_RELOC_SPARC_M44,            F_POP_V9 },
+  { "l44",             BFD_RELOC_SPARC_L44,            F_POP_V9 },
+  { "uhi",             BFD_RELOC_SPARC_HH22,           F_POP_V9 },
+  { "ulo",             BFD_RELOC_SPARC_HM10,           F_POP_V9 },
+  { "tgd_hi22",                BFD_RELOC_SPARC_TLS_GD_HI22,    0 },
+  { "tgd_lo10",                BFD_RELOC_SPARC_TLS_GD_LO10,    0 },
+  { "tldm_hi22",       BFD_RELOC_SPARC_TLS_LDM_HI22,   0 },
+  { "tldm_lo10",       BFD_RELOC_SPARC_TLS_LDM_LO10,   0 },
+  { "tldo_hix22",      BFD_RELOC_SPARC_TLS_LDO_HIX22,  0 },
+  { "tldo_lox10",      BFD_RELOC_SPARC_TLS_LDO_LOX10,  0 },
+  { "tie_hi22",                BFD_RELOC_SPARC_TLS_IE_HI22,    0 },
+  { "tie_lo10",                BFD_RELOC_SPARC_TLS_IE_LO10,    0 },
+  { "tle_hix22",       BFD_RELOC_SPARC_TLS_LE_HIX22,   0 },
+  { "tle_lox10",       BFD_RELOC_SPARC_TLS_LE_LOX10,   0 },
+  { "gdop_hix22",      BFD_RELOC_SPARC_GOTDATA_OP_HIX22, 0 },
+  { "gdop_lox10",      BFD_RELOC_SPARC_GOTDATA_OP_LOX10, 0 },
+  { "tgd_add",                 BFD_RELOC_SPARC_TLS_GD_ADD,     F_POP_POSTFIX },
+  { "tgd_call",                BFD_RELOC_SPARC_TLS_GD_CALL,    F_POP_POSTFIX|F_POP_TLS_CALL },
+  { "tldm_add",                BFD_RELOC_SPARC_TLS_LDM_ADD,    F_POP_POSTFIX },
+  { "tldm_call",       BFD_RELOC_SPARC_TLS_LDM_CALL,   F_POP_POSTFIX|F_POP_TLS_CALL },
+  { "tldo_add",                BFD_RELOC_SPARC_TLS_LDO_ADD,    F_POP_POSTFIX },
+  { "tie_ldx",         BFD_RELOC_SPARC_TLS_IE_LDX,     F_POP_POSTFIX },
+  { "tie_ld",          BFD_RELOC_SPARC_TLS_IE_LD,      F_POP_POSTFIX },
+  { "tie_add",         BFD_RELOC_SPARC_TLS_IE_ADD,     F_POP_POSTFIX },
+  { "gdop",            BFD_RELOC_SPARC_GOTDATA_OP,     F_POP_POSTFIX }
+};
+\f
+/* Table of %-names that can appear in a sparc assembly program.  This
+   table is initialized in md_begin and contains entries for each
+   privileged/hyperprivileged/alternate register and %-pseudo-op.  */
+
+enum perc_entry_type
+{
+  perc_entry_none = 0,
+  perc_entry_reg,
+  perc_entry_post_pop,
+  perc_entry_imm_pop
+};
+
+struct perc_entry
+{
+  /* Entry type.  */
+  enum perc_entry_type type;
+  /* Name of the %-entity.  */
+  const char *name;
+  /* strlen (name).  */
+  int len;
+  /* Value.  Either a pop or a reg depending on type.*/
+  union
+  {
+    struct pop_entry *pop;
+    struct priv_reg_entry *reg;
+  };
+};
+
+#define NUM_PERC_ENTRIES \
+  (((sizeof (priv_reg_table) / sizeof (priv_reg_table[0])) - 1)         \
+   + ((sizeof (hpriv_reg_table) / sizeof (hpriv_reg_table[0])) - 1)     \
+   + ((sizeof (v9a_asr_table) / sizeof (v9a_asr_table[0])) - 1)         \
+   + ARRAY_SIZE (pop_table)                                            \
+   + 1)
+
+struct perc_entry perc_table[NUM_PERC_ENTRIES];
+
+static int
+cmp_perc_entry (const void *parg, const void *qarg)
+{
+  const struct perc_entry *p = (const struct perc_entry *) parg;
+  const struct perc_entry *q = (const struct perc_entry *) qarg;
+
+  if (p->name == q->name)
+    return 0;
+  else if (p->name == NULL)
+    return 1;
+  else if (q->name == NULL)
+    return -1;
+  else
+    return strcmp (q->name, p->name);
 }
 \f
 /* This function is called once, at assembler startup time.  It should
@@ -863,9 +941,8 @@ cmp_reg_entry (const void *parg, const void *qarg)
 void
 md_begin (void)
 {
-  register const char *retval = NULL;
   int lose = 0;
-  register unsigned int i = 0;
+  unsigned int i = 0;
 
   /* We don't get a chance to initialize anything before md_parse_option
      is called, and it may not be called, so handle default initialization
@@ -874,16 +951,14 @@ md_begin (void)
     init_default_arch ();
 
   sparc_cie_data_alignment = sparc_arch_size == 64 ? -8 : -4;
-  op_hash = hash_new ();
+  op_hash = str_htab_create ();
 
   while (i < (unsigned int) sparc_num_opcodes)
     {
       const char *name = sparc_opcodes[i].name;
-      retval = hash_insert (op_hash, name, (void *) &sparc_opcodes[i]);
-      if (retval != NULL)
+      if (str_hash_insert (op_hash, name, &sparc_opcodes[i], 0) != NULL)
        {
-         as_bad (_("Internal error: can't hash `%s': %s\n"),
-                 sparc_opcodes[i].name, retval);
+         as_bad (_("duplicate %s"), name);
          lose = 1;
        }
       do
@@ -903,26 +978,20 @@ md_begin (void)
   for (i = 0; native_op_table[i].name; i++)
     {
       const struct sparc_opcode *insn;
-      char *name = ((sparc_arch_size == 32)
+      const char *name = ((sparc_arch_size == 32)
                    ? native_op_table[i].name32
                    : native_op_table[i].name64);
-      insn = (struct sparc_opcode *) hash_find (op_hash, name);
+      insn = (struct sparc_opcode *) str_hash_find (op_hash, name);
       if (insn == NULL)
        {
          as_bad (_("Internal error: can't find opcode `%s' for `%s'\n"),
                  name, native_op_table[i].name);
          lose = 1;
        }
-      else
+      else if (str_hash_insert (op_hash, native_op_table[i].name, insn, 0))
        {
-         retval = hash_insert (op_hash, native_op_table[i].name,
-                               (void *) insn);
-         if (retval != NULL)
-           {
-             as_bad (_("Internal error: can't hash `%s': %s\n"),
-                     sparc_opcodes[i].name, retval);
-             lose = 1;
-           }
+         as_bad (_("duplicate %s"), native_op_table[i].name);
+         lose = 1;
        }
     }
 
@@ -931,7 +1000,11 @@ md_begin (void)
 
   qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]),
         sizeof (priv_reg_table[0]), cmp_reg_entry);
-
+  qsort (hpriv_reg_table, sizeof (hpriv_reg_table) / sizeof (hpriv_reg_table[0]),
+        sizeof (hpriv_reg_table[0]), cmp_reg_entry);
+  qsort (v9a_asr_table, sizeof (v9a_asr_table) / sizeof (v9a_asr_table[0]),
+        sizeof (v9a_asr_table[0]), cmp_reg_entry);
+  
   /* If -bump, record the architecture level at which we start issuing
      warnings.  The behaviour is different depending upon whether an
      architecture was explicitly specified.  If it wasn't, we issue warnings
@@ -963,21 +1036,69 @@ md_begin (void)
                                     current_max_architecture))
        break;
   }
+
+  /* Prepare the tables of %-pseudo-ops.  */
+  {
+    struct priv_reg_entry *reg_tables[]
+      = {priv_reg_table, hpriv_reg_table, v9a_asr_table, NULL};
+    struct priv_reg_entry **reg_table;
+    int entry = 0;
+
+    /* Add registers.  */
+    for (reg_table = reg_tables; reg_table[0]; reg_table++)
+      {
+        struct priv_reg_entry *reg;
+        for (reg = *reg_table; reg->name; reg++)
+          {
+            struct perc_entry *p = &perc_table[entry++];
+            p->type = perc_entry_reg;
+            p->name = reg->name;
+            p->len = strlen (reg->name);
+            p->reg = reg;
+          }
+      }
+
+    /* Add %-pseudo-ops.  */
+    for (i = 0; i < ARRAY_SIZE (pop_table); i++)
+      {
+       struct perc_entry *p = &perc_table[entry++];
+       p->type = (pop_table[i].flags & F_POP_POSTFIX
+                  ? perc_entry_post_pop : perc_entry_imm_pop);
+       p->name = pop_table[i].name;
+       p->len = strlen (pop_table[i].name);
+       p->pop = &pop_table[i];
+      }
+
+    /* Last entry is the sentinel.  */
+    perc_table[entry].type = perc_entry_none;
+
+    qsort (perc_table, sizeof (perc_table) / sizeof (perc_table[0]),
+           sizeof (perc_table[0]), cmp_perc_entry);
+
+  }
 }
 
 /* Called after all assembly has been done.  */
 
 void
-sparc_md_end (void)
+sparc_md_finish (void)
 {
-  unsigned long mach = bfd_mach_sparc;
+  unsigned long mach;
+#ifndef TE_SOLARIS
   int hwcaps, hwcaps2;
+#endif
 
   if (sparc_arch_size == 64)
     switch (current_architecture)
       {
       case SPARC_OPCODE_ARCH_V9A: mach = bfd_mach_sparc_v9a; break;
       case SPARC_OPCODE_ARCH_V9B: mach = bfd_mach_sparc_v9b; break;
+      case SPARC_OPCODE_ARCH_V9C: mach = bfd_mach_sparc_v9c; break;
+      case SPARC_OPCODE_ARCH_V9D: mach = bfd_mach_sparc_v9d; break;
+      case SPARC_OPCODE_ARCH_V9E: mach = bfd_mach_sparc_v9e; break;
+      case SPARC_OPCODE_ARCH_V9V: mach = bfd_mach_sparc_v9v; break;
+      case SPARC_OPCODE_ARCH_V9M: mach = bfd_mach_sparc_v9m; break;
+      case SPARC_OPCODE_ARCH_M8:  mach = bfd_mach_sparc_v9m8; break;
       default: mach = bfd_mach_sparc_v9; break;
       }
   else
@@ -987,14 +1108,20 @@ sparc_md_end (void)
       case SPARC_OPCODE_ARCH_V9: mach = bfd_mach_sparc_v8plus; break;
       case SPARC_OPCODE_ARCH_V9A: mach = bfd_mach_sparc_v8plusa; break;
       case SPARC_OPCODE_ARCH_V9B: mach = bfd_mach_sparc_v8plusb; break;
+      case SPARC_OPCODE_ARCH_V9C: mach = bfd_mach_sparc_v8plusc; break;
+      case SPARC_OPCODE_ARCH_V9D: mach = bfd_mach_sparc_v8plusd; break;
+      case SPARC_OPCODE_ARCH_V9E: mach = bfd_mach_sparc_v8pluse; break;
+      case SPARC_OPCODE_ARCH_V9V: mach = bfd_mach_sparc_v8plusv; break;
+      case SPARC_OPCODE_ARCH_V9M: mach = bfd_mach_sparc_v8plusm; break;
+      case SPARC_OPCODE_ARCH_M8:  mach = bfd_mach_sparc_v8plusm8; break;
       /* The sparclite is treated like a normal sparc.  Perhaps it shouldn't
         be but for now it is (since that's the way it's always been
         treated).  */
-      default: break;
+      default: mach = bfd_mach_sparc; break;
       }
   bfd_set_arch_mach (stdoutput, bfd_arch_sparc, mach);
 
-#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+#ifndef TE_SOLARIS
   hwcaps = hwcap_seen & U0xffffffff;
   hwcaps2 = hwcap_seen >> 32;
 
@@ -1016,7 +1143,7 @@ in_signed_range (bfd_signed_vma val, bfd_signed_vma max)
      0xffffffff is always considered -1 on sparc32.  */
   if (sparc_arch_size == 32)
     {
-      bfd_signed_vma sign = (bfd_signed_vma) 1 << 31;
+      bfd_vma sign = (bfd_vma) 1 << 31;
       val = ((val & U0xffffffff) ^ sign) - sign;
     }
   if (val > max)
@@ -1074,10 +1201,10 @@ BSR (bfd_vma val, int amount)
 }
 \f
 /* For communication between sparc_ip and get_expression.  */
-static char *expr_end;
+static char *expr_parse_end;
 
 /* Values for `special_case'.
-   Instructions that require wierd handling because they're longer than
+   Instructions that require weird handling because they're longer than
    4 bytes.  */
 #define SPECIAL_CASE_NONE      0
 #define        SPECIAL_CASE_SET        1
@@ -1209,7 +1336,7 @@ synthetize_setsw (const struct sparc_opcode *insn)
   output_insn (insn, &the_insn);
 }
 
-/* Handle the setsw synthetic instruction.  */
+/* Handle the setx synthetic instruction.  */
 
 static void
 synthetize_setx (const struct sparc_opcode *insn)
@@ -1388,31 +1515,54 @@ md_assemble (char *str)
   if (insn == NULL)
     return;
 
-  /* We warn about attempts to put a floating point branch in a delay slot,
-     unless the delay slot has been annulled.  */
+  /* Certain instructions may not appear on delay slots.  Check for
+     these situations.  */
   if (last_insn != NULL
-      && (insn->flags & F_FBR) != 0
-      && (last_insn->flags & F_DELAYED) != 0
-      /* ??? This test isn't completely accurate.  We assume anything with
-        F_{UNBR,CONDBR,FBR} set is annullable.  */
-      && ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
-         || (last_opcode & ANNUL) == 0))
-    as_warn (_("FP branch in delay slot"));
-
-  /* SPARC before v9 requires a nop instruction between a floating
-     point instruction and a floating point branch.  We insert one
-     automatically, with a warning.  */
+      && (last_insn->flags & F_DELAYED) != 0)
+    {
+      /* Before SPARC V9 the effect of having a delayed branch
+         instruction in the delay slot of a conditional delayed branch
+         was undefined.
+
+         In SPARC V9 DCTI couples are well defined.
+
+         However, starting with the UltraSPARC Architecture 2005, DCTI
+         couples (of all kind) are deprecated and should not be used,
+         as they may be slow or behave differently to what the
+         programmer expects.  */
+      if (dcti_couples_detect
+          && (insn->flags & F_DELAYED) != 0
+          && ((max_architecture < SPARC_OPCODE_ARCH_V9
+               && (last_insn->flags & F_CONDBR) != 0)
+              || max_architecture >= SPARC_OPCODE_ARCH_V9C))
+        as_warn (_("unpredictable DCTI couple"));
+
+
+      /* We warn about attempts to put a floating point branch in a
+         delay slot, unless the delay slot has been annulled.  */
+      if ((insn->flags & F_FBR) != 0
+          /* ??? This test isn't completely accurate.  We assume anything with
+             F_{UNBR,CONDBR,FBR} set is annullable.  */
+          && ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
+              || (last_opcode & ANNUL) == 0))
+        as_warn (_("FP branch in delay slot"));
+    }
+
+  /* SPARC before v9 does not allow a floating point compare
+     directly before a floating point branch.  Insert a nop
+     instruction if needed, with a warning.  */
   if (max_architecture < SPARC_OPCODE_ARCH_V9
       && last_insn != NULL
       && (insn->flags & F_FBR) != 0
-      && (last_insn->flags & F_FLOAT) != 0)
+      && (last_insn->flags & F_FLOAT) != 0
+      && (last_insn->match & OP3 (0x35)) == OP3 (0x35))
     {
       struct sparc_it nop_insn;
 
       nop_insn.opcode = NOP_INSN;
       nop_insn.reloc = BFD_RELOC_NONE;
       output_insn (insn, &nop_insn);
-      as_warn (_("FP branch preceded by FP instruction; NOP inserted"));
+      as_warn (_("FP branch preceded by FP compare; NOP inserted"));
     }
 
   switch (special_case)
@@ -1457,7 +1607,7 @@ md_assemble (char *str)
 }
 
 static const char *
-get_hwcap_name (bfd_uint64_t mask)
+get_hwcap_name (uint64_t mask)
 {
   if (mask & HWCAP_MUL32)
     return "mul32";
@@ -1535,6 +1685,22 @@ get_hwcap_name (bfd_uint64_t mask)
     return "xmont";
   if (mask & HWCAP2_NSEC)
     return "nsec";
+  if (mask & HWCAP2_SPARC6)
+    return "sparc6";
+  if (mask & HWCAP2_ONADDSUB)
+    return "onaddsub";
+  if (mask & HWCAP2_ONMUL)
+    return "onmul";
+  if (mask & HWCAP2_ONDIV)
+    return "ondiv";
+  if (mask & HWCAP2_DICTUNP)
+    return "dictunp";
+  if (mask & HWCAP2_FPCMPSHL)
+    return "fpcmpshl";
+  if (mask & HWCAP2_RLE)
+    return "rle";
+  if (mask & HWCAP2_SHA3)
+    return "sha3";
 
   return "UNKNOWN";
 }
@@ -1544,7 +1710,7 @@ get_hwcap_name (bfd_uint64_t mask)
 static int
 sparc_ip (char *str, const struct sparc_opcode **pinsn)
 {
-  char *error_message = "";
+  const char *error_message = "";
   char *s;
   const char *args;
   char c;
@@ -1556,6 +1722,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
   int comma = 0;
   int v9_arg_p;
   int special_case = SPECIAL_CASE_NONE;
+  const sparc_asi *sasi = NULL;
 
   s = str;
   if (ISLOWER (*s))
@@ -1583,7 +1750,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
       *pinsn = NULL;
       return special_case;
     }
-  insn = (struct sparc_opcode *) hash_find (op_hash, str);
+  insn = (struct sparc_opcode *) str_hash_find (op_hash, str);
   *pinsn = insn;
   if (insn == NULL)
     {
@@ -1706,28 +1873,30 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              /* Parse a sparc64 privileged register.  */
              if (*s == '%')
                {
-                 struct priv_reg_entry *p = priv_reg_table;
+                 struct priv_reg_entry *p;
                  unsigned int len = 9999999; /* Init to make gcc happy.  */
 
                  s += 1;
-                 while (p->name[0] > s[0])
-                   p++;
-                 while (p->name[0] == s[0])
-                   {
-                     len = strlen (p->name);
-                     if (strncmp (p->name, s, len) == 0)
-                       break;
-                     p++;
-                   }
-                 if (p->name[0] != s[0])
+                  for (p = priv_reg_table; p->name; p++)
+                    if (p->name[0] == s[0])
+                      {
+                        len = strlen (p->name);
+                        if (strncmp (p->name, s, len) == 0)
+                          break;
+                      }
+
+                 if (!p->name)
                    {
                      error_message = _(": unrecognizable privileged register");
                      goto error;
                    }
-                 if (*args == '?')
-                   opcode |= (p->regnum << 14);
-                 else
-                   opcode |= (p->regnum << 25);
+                  
+                  if (((opcode >> (*args == '?' ? 14 : 25)) & 0x1f) != (unsigned) p->regnum)
+                    {
+                      error_message = _(": unrecognizable privileged register");
+                      goto error;
+                    }
+
                  s += len;
                  continue;
                }
@@ -1742,29 +1911,31 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              /* Parse a sparc64 hyperprivileged register.  */
              if (*s == '%')
                {
-                 struct priv_reg_entry *p = hpriv_reg_table;
+                 struct priv_reg_entry *p;
                  unsigned int len = 9999999; /* Init to make gcc happy.  */
 
                  s += 1;
-                 while (p->name[0] > s[0])
-                   p++;
-                 while (p->name[0] == s[0])
-                   {
-                     len = strlen (p->name);
-                     if (strncmp (p->name, s, len) == 0)
-                       break;
-                     p++;
-                   }
-                 if (p->name[0] != s[0])
+                  for (p = hpriv_reg_table; p->name; p++)
+                    if (p->name[0] == s[0])
+                      {
+                        len = strlen (p->name);
+                        if (strncmp (p->name, s, len) == 0)
+                          break;
+                      }
+
+                 if (!p->name)
                    {
                      error_message = _(": unrecognizable hyperprivileged register");
                      goto error;
                    }
-                 if (*args == '$')
-                   opcode |= (p->regnum << 14);
-                 else
-                   opcode |= (p->regnum << 25);
-                 s += len;
+
+                  if (((opcode >> (*args == '$' ? 14 : 25)) & 0x1f) != (unsigned) p->regnum)
+                    {
+                      error_message = _(": unrecognizable hyperprivileged register");
+                      goto error;
+                    }
+
+                  s += len;
                  continue;
                }
              else
@@ -1775,56 +1946,45 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
 
            case '_':
            case '/':
-             /* Parse a v9a/v9b ancillary state register.  */
+             /* Parse a v9a or later ancillary state register.  */
              if (*s == '%')
                {
-                 struct priv_reg_entry *p = v9a_asr_table;
+                 struct priv_reg_entry *p;
                  unsigned int len = 9999999; /* Init to make gcc happy.  */
 
                  s += 1;
-                 while (p->name[0] > s[0])
-                   p++;
-                 while (p->name[0] == s[0])
+                  for (p = v9a_asr_table; p->name; p++)
+                    if (p->name[0] == s[0])
+                      {
+                        len = strlen (p->name);
+                        if (strncmp (p->name, s, len) == 0)
+                          break;
+                      }
+
+                 if (!p->name)
                    {
-                     len = strlen (p->name);
-                     if (strncmp (p->name, s, len) == 0)
-                       break;
-                     p++;
-                   }
-                 if (p->name[0] != s[0])
-                   {
-                     error_message = _(": unrecognizable v9a or v9b ancillary state register");
-                     goto error;
-                   }
-                 if (*args == '/' && (p->regnum == 20 || p->regnum == 21))
-                   {
-                     error_message = _(": rd on write only ancillary state register");
+                     error_message = _(": unrecognizable ancillary state register");
                      goto error;
                    }
-                 if (p->regnum >= 24
-                     && (insn->architecture
-                         & SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A)))
-                   {
-                     /* %sys_tick and %sys_tick_cmpr are v9bnotv9a */
-                     error_message = _(": unrecognizable v9a ancillary state register");
-                     goto error;
-                   }
-                 if (*args == '/')
-                   opcode |= (p->regnum << 14);
-                 else
-                   opcode |= (p->regnum << 25);
+
+                  if (((opcode >> (*args == '/' ? 14 : 25)) & 0x1f) != (unsigned) p->regnum)
+                     {
+                       error_message = _(": unrecognizable ancillary state register");
+                       goto error;
+                     }
+
                  s += len;
                  continue;
                }
              else
                {
-                 error_message = _(": unrecognizable v9a or v9b ancillary state register");
+                 error_message = _(": unrecognizable ancillary state register");
                  goto error;
                }
 
            case 'M':
            case 'm':
-             if (strncmp (s, "%asr", 4) == 0)
+             if (startswith (s, "%asr"))
                {
                  s += 4;
 
@@ -1838,22 +1998,22 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                          ++s;
                        }
 
-                     if (current_architecture >= SPARC_OPCODE_ARCH_V9)
-                       {
-                         if (num < 16 || 31 < num)
-                           {
-                             error_message = _(": asr number must be between 16 and 31");
-                             goto error;
-                           }
-                       }
-                     else
-                       {
-                         if (num < 0 || 31 < num)
-                           {
-                             error_message = _(": asr number must be between 0 and 31");
-                             goto error;
-                           }
-                       }
+                      /* We used to check here for the asr number to
+                         be between 16 and 31 in V9 and later, as
+                         mandated by the section C.1.1 "Register
+                         Names" in the SPARC spec.  However, we
+                         decided to remove this restriction as a) it
+                         introduces problems when new V9 asr registers
+                         are introduced, b) the Solaris assembler
+                         doesn't implement this restriction and c) the
+                         restriction will go away in future revisions
+                         of the Oracle SPARC Architecture.  */
+
+                      if (num < 0 || 31 < num)
+                        {
+                          error_message = _(": asr number must be between 0 and 31");
+                          goto error;
+                        }
 
                      opcode |= (*args == 'M' ? RS1 (num) : RD (num));
                      continue;
@@ -1971,7 +2131,8 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                {
                  ++s;
                }
-             if (strncmp (s, "%icc", 4) == 0)
+             if ((startswith (s, "%icc"))
+                  || (sparc_arch_size == 32 && startswith (s, "%ncc")))
                {
                  s += 4;
                  continue;
@@ -1983,7 +2144,8 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                {
                  ++s;
                }
-             if (strncmp (s, "%xcc", 4) == 0)
+              if ((startswith (s, "%xcc"))
+                  || (sparc_arch_size == 64 && startswith (s, "%ncc")))
                {
                  s += 4;
                  continue;
@@ -1995,7 +2157,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                {
                  ++s;
                }
-             if (strncmp (s, "%fcc0", 5) == 0)
+             if (startswith (s, "%fcc0"))
                {
                  s += 5;
                  continue;
@@ -2007,7 +2169,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                {
                  ++s;
                }
-             if (strncmp (s, "%fcc1", 5) == 0)
+             if (startswith (s, "%fcc1"))
                {
                  s += 5;
                  continue;
@@ -2019,7 +2181,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                {
                  ++s;
                }
-             if (strncmp (s, "%fcc2", 5) == 0)
+             if (startswith (s, "%fcc2"))
                {
                  s += 5;
                  continue;
@@ -2031,7 +2193,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                {
                  ++s;
                }
-             if (strncmp (s, "%fcc3", 5) == 0)
+             if (startswith (s, "%fcc3"))
                {
                  s += 5;
                  continue;
@@ -2039,7 +2201,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              break;
 
            case 'P':
-             if (strncmp (s, "%pc", 3) == 0)
+             if (startswith (s, "%pc"))
                {
                  s += 3;
                  continue;
@@ -2047,7 +2209,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              break;
 
            case 'W':
-             if (strncmp (s, "%tick", 5) == 0)
+             if (startswith (s, "%tick"))
                {
                  s += 5;
                  continue;
@@ -2057,67 +2219,45 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
            case '\0':          /* End of args.  */
              if (s[0] == ',' && s[1] == '%')
                {
-                 static const struct ops
-                 {
-                   /* The name as it appears in assembler.  */
-                   char *name;
-                   /* strlen (name), precomputed for speed */
-                   int len;
-                   /* The reloc this pseudo-op translates to.  */
-                   int reloc;
-                   /* 1 if tls call.  */
-                   int tls_call;
-                 }
-                 ops[] =
-                 {
-                   { "tgd_add", 7, BFD_RELOC_SPARC_TLS_GD_ADD, 0 },
-                   { "tgd_call", 8, BFD_RELOC_SPARC_TLS_GD_CALL, 1 },
-                   { "tldm_add", 8, BFD_RELOC_SPARC_TLS_LDM_ADD, 0 },
-                   { "tldm_call", 9, BFD_RELOC_SPARC_TLS_LDM_CALL, 1 },
-                   { "tldo_add", 8, BFD_RELOC_SPARC_TLS_LDO_ADD, 0 },
-                   { "tie_ldx", 7, BFD_RELOC_SPARC_TLS_IE_LDX, 0 },
-                   { "tie_ld", 6, BFD_RELOC_SPARC_TLS_IE_LD, 0 },
-                   { "tie_add", 7, BFD_RELOC_SPARC_TLS_IE_ADD, 0 },
-                   { "gdop", 4, BFD_RELOC_SPARC_GOTDATA_OP, 0 },
-                   { NULL, 0, 0, 0 }
-                 };
-                 const struct ops *o;
                  char *s1;
                  int npar = 0;
+                  const struct perc_entry *p;
 
-                 for (o = ops; o->name; o++)
-                   if (strncmp (s + 2, o->name, o->len) == 0)
-                     break;
-                 if (o->name == NULL)
-                   break;
+                  for (p = perc_table; p->type != perc_entry_none; p++)
+                    if ((p->type == perc_entry_post_pop || p->type == perc_entry_reg)
+                        && strncmp (s + 2, p->name, p->len) == 0)
+                      break;
+                  if (p->type == perc_entry_none || p->type == perc_entry_reg)
+                    break;
 
-                 if (s[o->len + 2] != '(')
+                 if (s[p->len + 2] != '(')
                    {
-                     as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+                     as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name);
                      return special_case;
                    }
 
-                 if (! o->tls_call && the_insn.reloc != BFD_RELOC_NONE)
+                 if (! (p->pop->flags & F_POP_TLS_CALL)
+                      && the_insn.reloc != BFD_RELOC_NONE)
                    {
                      as_bad (_("Illegal operands: %%%s cannot be used together with other relocs in the insn ()"),
-                             o->name);
+                             p->name);
                      return special_case;
                    }
 
-                 if (o->tls_call
+                 if ((p->pop->flags & F_POP_TLS_CALL)
                      && (the_insn.reloc != BFD_RELOC_32_PCREL_S2
                          || the_insn.exp.X_add_number != 0
                          || the_insn.exp.X_add_symbol
                             != symbol_find_or_make ("__tls_get_addr")))
                    {
                      as_bad (_("Illegal operands: %%%s can be only used with call __tls_get_addr"),
-                             o->name);
+                             p->name);
                      return special_case;
                    }
 
-                 the_insn.reloc = o->reloc;
+                 the_insn.reloc = p->pop->reloc;
                  memset (&the_insn.exp, 0, sizeof (the_insn.exp));
-                 s += o->len + 3;
+                 s += p->len + 3;
 
                  for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
                    if (*s1 == '(')
@@ -2131,7 +2271,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
 
                  if (*s1 != ')')
                    {
-                     as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+                     as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name);
                      return special_case;
                    }
 
@@ -2176,7 +2316,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              break;
 
            case 'C':           /* Coprocessor state register.  */
-             if (strncmp (s, "%csr", 4) == 0)
+             if (startswith (s, "%csr"))
                {
                  s += 4;
                  continue;
@@ -2349,10 +2489,13 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
            case 'e':           /* next operand is a floating point register */
            case 'v':
            case 'V':
+            case ';':
 
            case 'f':
            case 'B':
            case 'R':
+            case ':':
+            case '\'':
 
            case '4':
            case '5':
@@ -2361,11 +2504,14 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
            case 'H':
            case 'J':
            case '}':
+            case '^':
              {
                char format;
 
                if (*s++ == '%'
-                   && ((format = *s) == 'f')
+                   && ((format = *s) == 'f'
+                        || format == 'd'
+                        || format == 'q')
                    && ISDIGIT (*++s))
                  {
                    for (mask = 0; ISDIGIT (*s); ++s)
@@ -2376,19 +2522,39 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                    if ((*args == 'v'
                         || *args == 'B'
                         || *args == '5'
-                        || *args == 'H')
+                        || *args == 'H'
+                         || *args == '\''
+                        || format == 'd')
                        && (mask & 1))
                      {
+                        /* register must be even numbered */
                        break;
-                     }         /* register must be even numbered */
+                     }
 
                    if ((*args == 'V'
                         || *args == 'R'
-                        || *args == 'J')
+                        || *args == 'J'
+                        || format == 'q')
                        && (mask & 3))
                      {
+                        /* register must be multiple of 4 */
                        break;
-                     }         /* register must be multiple of 4 */
+                     }
+
+                    if ((*args == ':'
+                         || *args == ';'
+                         || *args == '^')
+                        && (mask & 7))
+                      {
+                        /* register must be multiple of 8 */
+                        break;
+                      }
+
+                    if (*args == '\'' && mask < 48)
+                      {
+                        /* register must be higher or equal than %f48 */
+                        break;
+                      }
 
                    if (mask >= 64)
                      {
@@ -2435,15 +2601,21 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                  case 'v':
                  case 'V':
                  case 'e':
+                  case ';':
                    opcode |= RS1 (mask);
                    continue;
 
                  case 'f':
                  case 'B':
                  case 'R':
+                  case ':':
                    opcode |= RS2 (mask);
                    continue;
 
+                  case '\'':
+                    opcode |= RS2 (mask & 0xe);
+                    continue;
+                    
                  case '4':
                  case '5':
                    opcode |= RS3 (mask);
@@ -2453,6 +2625,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                  case 'H':
                  case 'J':
                  case '}':
+                  case '^':
                    opcode |= RD (mask);
                    continue;
                  }             /* Pack it in.  */
@@ -2462,7 +2635,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              }                 /* float arg  */
 
            case 'F':
-             if (strncmp (s, "%fsr", 4) == 0)
+             if (startswith (s, "%fsr"))
                {
                  s += 4;
                  continue;
@@ -2470,7 +2643,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              break;
 
            case '(':
-             if (strncmp (s, "%efsr", 5) == 0)
+             if (startswith (s, "%efsr"))
                {
                  s += 5;
                  continue;
@@ -2507,79 +2680,32 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
 
              {
                char *s1;
-               char *op_arg = NULL;
+               const char *op_arg = NULL;
                static expressionS op_exp;
                bfd_reloc_code_real_type old_reloc = the_insn.reloc;
 
                /* Check for %hi, etc.  */
                if (*s == '%')
                  {
-                   static const struct ops {
-                     /* The name as it appears in assembler.  */
-                     char *name;
-                     /* strlen (name), precomputed for speed */
-                     int len;
-                     /* The reloc this pseudo-op translates to.  */
-                     int reloc;
-                     /* Non-zero if for v9 only.  */
-                     int v9_p;
-                     /* Non-zero if can be used in pc-relative contexts.  */
-                     int pcrel_p;/*FIXME:wip*/
-                   } ops[] = {
-                     /* hix/lox must appear before hi/lo so %hix won't be
-                        mistaken for %hi.  */
-                     { "hix", 3, BFD_RELOC_SPARC_HIX22, 1, 0 },
-                     { "lox", 3, BFD_RELOC_SPARC_LOX10, 1, 0 },
-                     { "hi", 2, BFD_RELOC_HI22, 0, 1 },
-                     { "lo", 2, BFD_RELOC_LO10, 0, 1 },
-                     { "pc22", 4, BFD_RELOC_SPARC_PC22, 0, 1 },
-                     { "pc10", 4, BFD_RELOC_SPARC_PC10, 0, 1 },
-                     { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 },
-                     { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 },
-                     { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 },
-                     { "h34", 3, BFD_RELOC_SPARC_H34, 1, 0 },
-                     { "l34", 3, BFD_RELOC_SPARC_L44, 1, 0 },
-                     { "h44", 3, BFD_RELOC_SPARC_H44, 1, 0 },
-                     { "m44", 3, BFD_RELOC_SPARC_M44, 1, 0 },
-                     { "l44", 3, BFD_RELOC_SPARC_L44, 1, 0 },
-                     { "uhi", 3, BFD_RELOC_SPARC_HH22, 1, 0 },
-                     { "ulo", 3, BFD_RELOC_SPARC_HM10, 1, 0 },
-                     { "tgd_hi22", 8, BFD_RELOC_SPARC_TLS_GD_HI22, 0, 0 },
-                     { "tgd_lo10", 8, BFD_RELOC_SPARC_TLS_GD_LO10, 0, 0 },
-                     { "tldm_hi22", 9, BFD_RELOC_SPARC_TLS_LDM_HI22, 0, 0 },
-                     { "tldm_lo10", 9, BFD_RELOC_SPARC_TLS_LDM_LO10, 0, 0 },
-                     { "tldo_hix22", 10, BFD_RELOC_SPARC_TLS_LDO_HIX22, 0,
-                                                                        0 },
-                     { "tldo_lox10", 10, BFD_RELOC_SPARC_TLS_LDO_LOX10, 0,
-                                                                        0 },
-                     { "tie_hi22", 8, BFD_RELOC_SPARC_TLS_IE_HI22, 0, 0 },
-                     { "tie_lo10", 8, BFD_RELOC_SPARC_TLS_IE_LO10, 0, 0 },
-                     { "tle_hix22", 9, BFD_RELOC_SPARC_TLS_LE_HIX22, 0, 0 },
-                     { "tle_lox10", 9, BFD_RELOC_SPARC_TLS_LE_LOX10, 0, 0 },
-                     { "gdop_hix22", 10, BFD_RELOC_SPARC_GOTDATA_OP_HIX22,
-                       0, 0 },
-                     { "gdop_lox10", 10, BFD_RELOC_SPARC_GOTDATA_OP_LOX10,
-                       0, 0 },
-                     { NULL, 0, 0, 0, 0 }
-                   };
-                   const struct ops *o;
-
-                   for (o = ops; o->name; o++)
-                     if (strncmp (s + 1, o->name, o->len) == 0)
-                       break;
-                   if (o->name == NULL)
-                     break;
-
-                   if (s[o->len + 1] != '(')
+                    const struct perc_entry *p;
+                    
+                    for (p = perc_table; p->type != perc_entry_none; p++)
+                      if ((p->type == perc_entry_imm_pop || p->type == perc_entry_reg)
+                          && strncmp (s + 1, p->name, p->len) == 0)
+                        break;
+                    if (p->type == perc_entry_none || p->type == perc_entry_reg)
+                      break;
+
+                   if (s[p->len + 1] != '(')
                      {
-                       as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+                       as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name);
                        return special_case;
                      }
 
-                   op_arg = o->name;
-                   the_insn.reloc = o->reloc;
-                   s += o->len + 2;
-                   v9_arg_p = o->v9_p;
+                   op_arg = p->name;
+                   the_insn.reloc = p->pop->reloc;
+                   s += p->len + 2;
+                   v9_arg_p = p->pop->flags & F_POP_V9;
                  }
 
                /* Note that if the get_expression() fails, we will still
@@ -2615,6 +2741,11 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                    *s1 = '\0';
                    (void) get_expression (s);
                    *s1 = ')';
+                   if (expr_parse_end != s1)
+                     {
+                       as_bad (_("Expression inside %%%s could not be parsed"), op_arg);
+                       return special_case;
+                     }
                    s = s1 + 1;
                    if (*s == ',' || *s == ']' || !*s)
                      continue;
@@ -2663,7 +2794,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                    (void) get_expression (s);
                    if (op_arg)
                      *s = ')';
-                   s = expr_end;
+                   s = expr_parse_end;
                  }
 
                if (op_arg)
@@ -2792,17 +2923,18 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                     handle the R_SPARC_5 immediate directly here so that
                     we don't need to add support for multiple relocations
                     in one instruction just yet.  */
-                 if (the_insn.reloc == BFD_RELOC_SPARC_5)
+                 if (the_insn.reloc == BFD_RELOC_SPARC_5
+                      && ((insn->match & OP(0x3)) == 0))
                    {
                      valueT val = the_insn.exp.X_add_number;
 
+                     the_insn.reloc = BFD_RELOC_NONE;
                      if (! in_bitfield_range (val, 0x1f))
                        {
                          error_message = _(": Immediate value in cbcond is out of range.");
                          goto error;
                        }
                      opcode |= val & 0x1f;
-                     the_insn.reloc = BFD_RELOC_NONE;
                    }
                }
 
@@ -2823,11 +2955,12 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                /* Parse an asi.  */
                if (*s == '#')
                  {
-                   if (! parse_keyword_arg (sparc_encode_asi, &s, &asi))
+                   if (! parse_sparc_asi (&s, &sasi))
                      {
                        error_message = _(": invalid ASI name");
                        goto error;
                      }
+                   asi = sasi->value;
                  }
                else
                  {
@@ -2847,7 +2980,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              }                 /* Alternate space.  */
 
            case 'p':
-             if (strncmp (s, "%psr", 4) == 0)
+             if (startswith (s, "%psr"))
                {
                  s += 4;
                  continue;
@@ -2855,7 +2988,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              break;
 
            case 'q':           /* Floating point queue.  */
-             if (strncmp (s, "%fq", 3) == 0)
+             if (startswith (s, "%fq"))
                {
                  s += 3;
                  continue;
@@ -2863,7 +2996,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              break;
 
            case 'Q':           /* Coprocessor queue.  */
-             if (strncmp (s, "%cq", 3) == 0)
+             if (startswith (s, "%cq"))
                {
                  s += 3;
                  continue;
@@ -2887,7 +3020,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                  special_case = SPECIAL_CASE_SETX;
                  continue;
                }
-             else if (strncmp (str, "fdiv", 4) == 0)
+             else if (startswith (str, "fdiv"))
                {
                  special_case = SPECIAL_CASE_FDIV;
                  continue;
@@ -2895,41 +3028,67 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              break;
 
            case 'o':
-             if (strncmp (s, "%asi", 4) != 0)
+             if (!startswith (s, "%asi"))
                break;
              s += 4;
              continue;
 
            case 's':
-             if (strncmp (s, "%fprs", 5) != 0)
+             if (!startswith (s, "%fprs"))
                break;
              s += 5;
              continue;
 
            case '{':
-             if (strncmp (s, "%mcdper",7) != 0)
+             if (!startswith (s, "%mcdper"))
                break;
              s += 7;
              continue;
 
+            case '&':
+              if (!startswith (s, "%entropy"))
+                break;
+              s += 8;
+              continue;
+
            case 'E':
-             if (strncmp (s, "%ccr", 4) != 0)
+             if (!startswith (s, "%ccr"))
                break;
              s += 4;
              continue;
 
            case 't':
-             if (strncmp (s, "%tbr", 4) != 0)
+             if (!startswith (s, "%tbr"))
                break;
              s += 4;
              continue;
 
            case 'w':
-             if (strncmp (s, "%wim", 4) != 0)
+             if (!startswith (s, "%wim"))
                break;
              s += 4;
              continue;
 
+            case '|':
+              {
+                int imm2 = 0;
+
+                /* Parse a 2-bit immediate.  */
+                if (! parse_const_expr_arg (&s, &imm2))
+                  {
+                    error_message = _(": non-immdiate imm2 operand");
+                    goto error;
+                  }
+                if ((imm2 & ~0x3) != 0)
+                  {
+                    error_message = _(": imm2 immediate operand out of range (0-3)");
+                    goto error;
+                  }
+
+                opcode |= ((imm2 & 0x2) << 3) | (imm2 & 0x1);
+                continue;
+              }
+              
            case 'x':
              {
                char *push = input_line_pointer;
@@ -2953,7 +3112,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
              }
 
            case 'y':
-             if (strncmp (s, "%y", 2) != 0)
+             if (!startswith (s, "%y"))
                break;
              s += 2;
              continue;
@@ -3001,11 +3160,20 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
       else
        {
          /* We have a match.  Now see if the architecture is OK.  */
+         /* String to use in case of architecture warning.  */
+         const char *msg_str = str;
          int needed_arch_mask = insn->architecture;
-         bfd_uint64_t hwcaps
-           = (((bfd_uint64_t) insn->hwcaps2) << 32) | insn->hwcaps;
 
-#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+          /* Include the ASI architecture needed as well */
+          if (sasi && needed_arch_mask > sasi->architecture)
+            {
+              needed_arch_mask = sasi->architecture;
+              msg_str = sasi->name;
+            }
+
+         uint64_t hwcaps = ((uint64_t) insn->hwcaps2 << 32) | insn->hwcaps;
+
+#ifndef TE_SOLARIS
          if (hwcaps)
                  hwcap_seen |= hwcaps;
 #endif
@@ -3037,11 +3205,15 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                  as_warn (_("architecture bumped from \"%s\" to \"%s\" on \"%s\""),
                           sparc_opcode_archs[current_architecture].name,
                           sparc_opcode_archs[needed_architecture].name,
-                          str);
+                          msg_str);
                  warn_after_architecture = needed_architecture;
                }
              current_architecture = needed_architecture;
-             hwcap_allowed |= hwcaps;
+             hwcap_allowed
+               = (hwcap_allowed
+                  | hwcaps
+                  | ((uint64_t) sparc_opcode_archs[current_architecture].hwcaps2 << 32)
+                  | sparc_opcode_archs[current_architecture].hwcaps);
            }
          /* Conflict.  */
          /* ??? This seems to be a bit fragile.  What if the next entry in
@@ -3071,8 +3243,8 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                  ++arch;
                }
 
-             as_bad (_("Architecture mismatch on \"%s\"."), str);
-             as_tsktsk (_(" (Requires %s; requested architecture is %s.)"),
+             as_bad (_("Architecture mismatch on \"%s %s\"."), str, argsStart);
+             as_tsktsk (_("(Requires %s; requested architecture is %s.)"),
                         required_archs,
                         sparc_opcode_archs[max_architecture].name);
              return special_case;
@@ -3097,6 +3269,35 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
   return special_case;
 }
 
+static char *
+skip_over_keyword (char *q)
+{
+  for (q = q + (*q == '#' || *q == '%');
+       ISALNUM (*q) || *q == '_';
+       ++q)
+    continue;
+  return q;
+}
+
+static int
+parse_sparc_asi (char **input_pointer_p, const sparc_asi **value_p)
+{
+  const sparc_asi *value;
+  char c, *p, *q;
+
+  p = *input_pointer_p;
+  q = skip_over_keyword(p);
+  c = *q;
+  *q = 0;
+  value = sparc_encode_asi (p);
+  *q = c;
+  if (value == NULL)
+    return 0;
+  *value_p = value;
+  *input_pointer_p = q;
+  return 1;
+}
+
 /* Parse an argument that can be expressed as a keyword.
    (eg: #StoreStore or %ccfr).
    The result is a boolean indicating success.
@@ -3111,10 +3312,7 @@ parse_keyword_arg (int (*lookup_fn) (const char *),
   char c, *p, *q;
 
   p = *input_pointerP;
-  for (q = p + (*p == '#' || *p == '%');
-       ISALNUM (*q) || *q == '_';
-       ++q)
-    continue;
+  q = skip_over_keyword(p);
   c = *q;
   *q = 0;
   value = (*lookup_fn) (p);
@@ -3175,11 +3373,11 @@ get_expression (char *str)
       && seg != undefined_section)
     {
       the_insn.error = _("bad segment");
-      expr_end = input_line_pointer;
+      expr_parse_end = input_line_pointer;
       input_line_pointer = save_in;
       return 1;
     }
-  expr_end = input_line_pointer;
+  expr_parse_end = input_line_pointer;
   input_line_pointer = save_in;
   return 0;
 }
@@ -3218,12 +3416,10 @@ output_insn (const struct sparc_opcode *insn, struct sparc_it *theinsn)
   last_insn = insn;
   last_opcode = theinsn->opcode;
 
-#ifdef OBJ_ELF
   dwarf2_emit_insn (4);
-#endif
 }
 \f
-char *
+const char *
 md_atof (int type, char *litP, int *sizeP)
 {
   return ieee_md_atof (type, litP, sizeP, target_big_endian);
@@ -3260,7 +3456,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT segment ATTRIBUTE_UNUSED)
 
   fixP->fx_addnumber = val;    /* Remember value for emit_reloc.  */
 
-#ifdef OBJ_ELF
   /* SPARC ELF relocations don't use an addend in the data field.  */
   if (fixP->fx_addsy != NULL)
     {
@@ -3298,7 +3493,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT segment ATTRIBUTE_UNUSED)
 
       return;
     }
-#endif
 
   /* This is a hack.  There should be a better way to
      handle this.  Probably in terms of howto fields, once
@@ -3306,45 +3500,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT segment ATTRIBUTE_UNUSED)
   if (fixP->fx_r_type == BFD_RELOC_32_PCREL_S2 && fixP->fx_addsy)
     val += fixP->fx_where + fixP->fx_frag->fr_address;
 
-#ifdef OBJ_AOUT
-  /* FIXME: More ridiculous gas reloc hacking.  If we are going to
-     generate a reloc, then we just want to let the reloc addend set
-     the value.  We do not want to also stuff the addend into the
-     object file.  Including the addend in the object file works when
-     doing a static link, because the linker will ignore the object
-     file contents.  However, the dynamic linker does not ignore the
-     object file contents.  */
-  if (fixP->fx_addsy != NULL
-      && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2)
-    val = 0;
-
-  /* When generating PIC code, we do not want an addend for a reloc
-     against a local symbol.  We adjust fx_addnumber to cancel out the
-     value already included in val, and to also cancel out the
-     adjustment which bfd_install_relocation will create.  */
-  if (sparc_pic_code
-      && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2
-      && fixP->fx_addsy != NULL
-      && ! S_IS_COMMON (fixP->fx_addsy)
-      && symbol_section_p (fixP->fx_addsy))
-    fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
-
-  /* When generating PIC code, we need to fiddle to get
-     bfd_install_relocation to do the right thing for a PC relative
-     reloc against a local symbol which we are going to keep.  */
-  if (sparc_pic_code
-      && fixP->fx_r_type == BFD_RELOC_32_PCREL_S2
-      && fixP->fx_addsy != NULL
-      && (S_IS_EXTERNAL (fixP->fx_addsy)
-         || S_IS_WEAK (fixP->fx_addsy))
-      && S_IS_DEFINED (fixP->fx_addsy)
-      && ! S_IS_COMMON (fixP->fx_addsy))
-    {
-      val = 0;
-      fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
-    }
-#endif
-
   /* If this is a data relocation, just output VAL.  */
 
   if (fixP->fx_r_type == BFD_RELOC_8)
@@ -3395,8 +3550,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT segment ATTRIBUTE_UNUSED)
 
          insn |= val & 0x3fffffff;
 
-         /* See if we have a delay slot.  */
-         if (sparc_relax && fixP->fx_where + 8 <= fixP->fx_frag->fr_fix)
+         /* See if we have a delay slot.  In that case we attempt to
+             optimize several cases transforming CALL instructions
+             into branches.  But we can only do that if the relocation
+             can be completely resolved here, i.e. if no undefined
+             symbol is associated with it.  */
+         if (sparc_relax && fixP->fx_addsy == NULL
+             && fixP->fx_where + 8 <= fixP->fx_frag->fr_fix)
            {
 #define G0             0
 #define O7             15
@@ -3669,17 +3829,42 @@ tc_gen_reloc (asection *section, fixS *fixp)
   arelent *reloc;
   bfd_reloc_code_real_type code;
 
-  relocs[0] = reloc = (arelent *) xmalloc (sizeof (arelent));
+  relocs[0] = reloc = XNEW (arelent);
   relocs[1] = NULL;
 
-  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  reloc->sym_ptr_ptr = XNEW (asymbol *);
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
   switch (fixp->fx_r_type)
     {
+    case BFD_RELOC_8:
     case BFD_RELOC_16:
     case BFD_RELOC_32:
+    case BFD_RELOC_64:
+      if (fixp->fx_pcrel)
+       {
+         switch (fixp->fx_size)
+           {
+           default:
+             as_bad_where (fixp->fx_file, fixp->fx_line,
+                           _("can not do %d byte pc-relative relocation"),
+                           fixp->fx_size);
+             code = fixp->fx_r_type;
+             fixp->fx_pcrel = 0;
+             break;
+           case 1: code = BFD_RELOC_8_PCREL;  break;
+           case 2: code = BFD_RELOC_16_PCREL; break;
+           case 4: code = BFD_RELOC_32_PCREL; break;
+#ifdef BFD64
+           case 8: code = BFD_RELOC_64_PCREL; break;
+#endif
+           }
+         if (fixp->fx_pcrel)
+           fixp->fx_addnumber = fixp->fx_offset;
+         break;
+       }
+      /* Fall through.  */
     case BFD_RELOC_HI22:
     case BFD_RELOC_LO10:
     case BFD_RELOC_32_PCREL_S2:
@@ -3692,7 +3877,6 @@ tc_gen_reloc (asection *section, fixS *fixp)
     case BFD_RELOC_SPARC_WDISP16:
     case BFD_RELOC_SPARC_WDISP19:
     case BFD_RELOC_SPARC_WDISP22:
-    case BFD_RELOC_64:
     case BFD_RELOC_SPARC_5:
     case BFD_RELOC_SPARC_6:
     case BFD_RELOC_SPARC_7:
@@ -3753,21 +3937,16 @@ tc_gen_reloc (asection *section, fixS *fixp)
       return NULL;
     }
 
-#if defined (OBJ_ELF) || defined (OBJ_AOUT)
   /* If we are generating PIC code, we need to generate a different
      set of relocs.  */
 
-#ifdef OBJ_ELF
 #define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
-#else
-#define GOT_NAME "__GLOBAL_OFFSET_TABLE_"
-#endif
 #ifdef TE_VXWORKS
 #define GOTT_BASE "__GOTT_BASE__"
 #define GOTT_INDEX "__GOTT_INDEX__"
 #endif
 
-  /* This code must be parallel to the OBJ_ELF tc_fix_adjustable.  */
+  /* This code must be parallel to tc_fix_adjustable.  */
 
   if (sparc_pic_code)
     {
@@ -3810,10 +3989,9 @@ tc_gen_reloc (asection *section, fixS *fixp)
          break;
        }
     }
-#endif /* defined (OBJ_ELF) || defined (OBJ_AOUT)  */
 
   /* Nothing is aligned in DWARF debugging sections.  */
-  if (bfd_get_section_flags (stdoutput, section) & SEC_DEBUGGING)
+  if (bfd_section_flags (section) & SEC_DEBUGGING)
     switch (code)
       {
       case BFD_RELOC_16: code = BFD_RELOC_SPARC_UA16; break;
@@ -3837,25 +4015,6 @@ tc_gen_reloc (asection *section, fixS *fixp)
     }
 
   /* @@ Why fx_addnumber sometimes and fx_offset other times?  */
-#ifdef OBJ_AOUT
-
-  if (reloc->howto->pc_relative == 0
-      || code == BFD_RELOC_SPARC_PC10
-      || code == BFD_RELOC_SPARC_PC22)
-    reloc->addend = fixp->fx_addnumber;
-  else if (sparc_pic_code
-          && fixp->fx_r_type == BFD_RELOC_32_PCREL_S2
-          && fixp->fx_addsy != NULL
-          && (S_IS_EXTERNAL (fixp->fx_addsy)
-              || S_IS_WEAK (fixp->fx_addsy))
-          && S_IS_DEFINED (fixp->fx_addsy)
-          && ! S_IS_COMMON (fixp->fx_addsy))
-    reloc->addend = fixp->fx_addnumber;
-  else
-    reloc->addend = fixp->fx_offset - reloc->address;
-
-#else /* elf or coff  */
-
   if (code != BFD_RELOC_32_PCREL_S2
       && code != BFD_RELOC_SPARC_WDISP22
       && code != BFD_RELOC_SPARC_WDISP16
@@ -3871,16 +4030,15 @@ tc_gen_reloc (asection *section, fixS *fixp)
                     + md_pcrel_from (fixp));
   else
     reloc->addend = fixp->fx_offset;
-#endif
 
   /* We expand R_SPARC_OLO10 to R_SPARC_LO10 and R_SPARC_13
      on the same location.  */
   if (code == BFD_RELOC_SPARC_OLO10)
     {
-      relocs[1] = reloc = (arelent *) xmalloc (sizeof (arelent));
+      relocs[1] = reloc = XNEW (arelent);
       relocs[2] = NULL;
 
-      reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+      reloc->sym_ptr_ptr = XNEW (asymbol *);
       *reloc->sym_ptr_ptr
        = symbol_get_bfdsym (section_symbol (absolute_section));
       reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
@@ -3904,20 +4062,7 @@ md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
 valueT
 md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
 {
-#ifndef OBJ_ELF
-  /* This is not right for ELF; a.out wants it, and COFF will force
-     the alignment anyways.  */
-  valueT align = ((valueT) 1
-                 << (valueT) bfd_get_section_alignment (stdoutput, segment));
-  valueT newsize;
-
-  /* Turn alignment value into a mask.  */
-  align--;
-  newsize = (size + align) & ~align;
-  return newsize;
-#else
   return size;
-#endif
 }
 
 /* Exactly what point is a PC-relative offset relative TO?
@@ -3956,10 +4101,6 @@ mylog2 (int value)
 
 /* Sort of like s_lcomm.  */
 
-#ifndef OBJ_ELF
-static int max_alignment = 15;
-#endif
-
 static void
 s_reserve (int ignore ATTRIBUTE_UNUSED)
 {
@@ -3971,11 +4112,10 @@ s_reserve (int ignore ATTRIBUTE_UNUSED)
   int temp;
   symbolS *symbolP;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (&name);
   p = input_line_pointer;
   *p = c;
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
 
   if (*input_line_pointer != ',')
     {
@@ -3997,8 +4137,8 @@ s_reserve (int ignore ATTRIBUTE_UNUSED)
   symbolP = symbol_find_or_make (name);
   *p = c;
 
-  if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0
-      && strncmp (input_line_pointer, ",\".bss\"", 7) != 0)
+  if (!startswith (input_line_pointer, ",\"bss\"")
+      && !startswith (input_line_pointer, ",\".bss\""))
     {
       as_bad (_("bad .reserve segment -- expected BSS segment"));
       return;
@@ -4024,14 +4164,6 @@ s_reserve (int ignore ATTRIBUTE_UNUSED)
 
       align = (int) get_absolute_expression ();
 
-#ifndef OBJ_ELF
-      if (align > max_alignment)
-       {
-         align = max_alignment;
-         as_warn (_("alignment too large; assuming %d"), align);
-       }
-#endif
-
       if (align < 0)
        {
          as_bad (_("negative alignment"));
@@ -4057,12 +4189,7 @@ s_reserve (int ignore ATTRIBUTE_UNUSED)
   else
     align = 0;
 
-  if (!S_IS_DEFINED (symbolP)
-#ifdef OBJ_AOUT
-      && S_GET_OTHER (symbolP) == 0
-      && S_GET_DESC (symbolP) == 0
-#endif
-      )
+  if (!S_IS_DEFINED (symbolP))
     {
       if (! need_pass_2)
        {
@@ -4090,9 +4217,7 @@ s_reserve (int ignore ATTRIBUTE_UNUSED)
 
          subseg_set (current_seg, current_subseg);
 
-#ifdef OBJ_ELF
          S_SET_SIZE (symbolP, size);
-#endif
        }
     }
   else
@@ -4113,12 +4238,11 @@ s_common (int ignore ATTRIBUTE_UNUSED)
   offsetT temp, size;
   symbolS *symbolP;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (&name);
   /* Just after name is now '\0'.  */
   p = input_line_pointer;
   *p = c;
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
   if (*input_line_pointer != ',')
     {
       as_bad (_("Expected comma after symbol-name"));
@@ -4154,13 +4278,6 @@ s_common (int ignore ATTRIBUTE_UNUSED)
                   S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), (long) size);
        }
     }
-  else
-    {
-#ifndef OBJ_ELF
-      S_SET_VALUE (symbolP, (valueT) size);
-      S_SET_EXTERNAL (symbolP);
-#endif
-    }
   know (symbol_get_frag (symbolP) == &zero_address_frag);
   if (*input_line_pointer != ',')
     {
@@ -4174,14 +4291,6 @@ s_common (int ignore ATTRIBUTE_UNUSED)
     {
       temp = get_absolute_expression ();
 
-#ifndef OBJ_ELF
-      if (temp > max_alignment)
-       {
-         temp = max_alignment;
-         as_warn (_("alignment too large; assuming %ld"), (long) temp);
-       }
-#endif
-
       if (temp < 0)
        {
          as_bad (_("negative alignment"));
@@ -4189,7 +4298,6 @@ s_common (int ignore ATTRIBUTE_UNUSED)
          return;
        }
 
-#ifdef OBJ_ELF
       if (symbol_get_obj (symbolP)->local)
        {
          segT old_sec;
@@ -4227,14 +4335,11 @@ s_common (int ignore ATTRIBUTE_UNUSED)
          subseg_set (old_sec, old_subsec);
        }
       else
-#endif /* OBJ_ELF  */
        {
        allocate_common:
          S_SET_VALUE (symbolP, (valueT) size);
-#ifdef OBJ_ELF
          S_SET_ALIGN (symbolP, temp);
          S_SET_SIZE (symbolP, size);
-#endif
          S_SET_EXTERNAL (symbolP);
          S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
        }
@@ -4246,8 +4351,8 @@ s_common (int ignore ATTRIBUTE_UNUSED)
       if (*input_line_pointer == '.')
        input_line_pointer++;
       /* @@ Some say data, some say bss.  */
-      if (strncmp (input_line_pointer, "bss\"", 4)
-         && strncmp (input_line_pointer, "data\"", 5))
+      if (!startswith (input_line_pointer, "bss\"")
+         && !startswith (input_line_pointer, "data\""))
        {
          while (*--input_line_pointer != '"')
            ;
@@ -4294,25 +4399,25 @@ static void
 s_seg (int ignore ATTRIBUTE_UNUSED)
 {
 
-  if (strncmp (input_line_pointer, "\"text\"", 6) == 0)
+  if (startswith (input_line_pointer, "\"text\""))
     {
       input_line_pointer += 6;
       s_text (0);
       return;
     }
-  if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
+  if (startswith (input_line_pointer, "\"data\""))
     {
       input_line_pointer += 6;
       s_data (0);
       return;
     }
-  if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
+  if (startswith (input_line_pointer, "\"data1\""))
     {
       input_line_pointer += 7;
       s_data1 ();
       return;
     }
-  if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
+  if (startswith (input_line_pointer, "\"bss\""))
     {
       input_line_pointer += 5;
       /* We only support 2 segments -- text and data -- for now, so
@@ -4370,7 +4475,6 @@ s_ncons (int bytes ATTRIBUTE_UNUSED)
   cons (sparc_arch_size == 32 ? 4 : 8);
 }
 
-#ifdef OBJ_ELF
 /* Handle the SPARC ELF .register pseudo-op.  This sets the binding of a
    global register.
    The syntax is:
@@ -4384,7 +4488,7 @@ s_register (int ignore ATTRIBUTE_UNUSED)
   char c;
   int reg;
   int flags;
-  const char *regname;
+  char *regname;
 
   if (input_line_pointer[0] != '%'
       || input_line_pointer[1] != 'g'
@@ -4398,20 +4502,19 @@ s_register (int ignore ATTRIBUTE_UNUSED)
   if (*input_line_pointer == '#')
     {
       ++input_line_pointer;
-      regname = input_line_pointer;
-      c = get_symbol_end ();
+      c = get_symbol_name (&regname);
       if (strcmp (regname, "scratch") && strcmp (regname, "ignore"))
        as_bad (_("register syntax is .register %%g[2367],{#scratch|symbolname|#ignore}"));
       if (regname[0] == 'i')
        regname = NULL;
       else
-       regname = "";
+       regname = (char *) "";
     }
   else
     {
-      regname = input_line_pointer;
-      c = get_symbol_end ();
+      c = get_symbol_name (&regname);
     }
+
   if (sparc_arch_size == 64)
     {
       if (globals[reg])
@@ -4458,7 +4561,7 @@ s_register (int ignore ATTRIBUTE_UNUSED)
        }
     }
 
-  *input_line_pointer = c;
+  (void) restore_line_pointer (c);
 
   demand_empty_rest_of_line ();
 }
@@ -4484,7 +4587,6 @@ sparc_adjust_symtab (void)
       S_SET_SEGMENT (sym, undefined_section);
     }
 }
-#endif
 
 /* If the --enforce-aligned-data option is used, we require .word,
    et. al., to be aligned correctly.  We do it by setting up an
@@ -4584,7 +4686,6 @@ sparc_handle_align (fragS *fragp)
     }
 }
 
-#ifdef OBJ_ELF
 /* Some special processing for a Sparc ELF file.  */
 
 void
@@ -4626,12 +4727,12 @@ sparc_cons (expressionS *exp, int size)
       && input_line_pointer[1] == 'r'
       && input_line_pointer[2] == '_')
     {
-      if (strncmp (input_line_pointer + 3, "disp", 4) == 0)
+      if (startswith (input_line_pointer + 3, "disp"))
        {
          input_line_pointer += 7;
          sparc_cons_special_reloc = "disp";
        }
-      else if (strncmp (input_line_pointer + 3, "plt", 3) == 0)
+      else if (startswith (input_line_pointer + 3, "plt"))
        {
          if (size != 4 && size != 8)
            as_bad (_("Illegal operands: %%r_plt in %d-byte data field"), size);
@@ -4641,7 +4742,7 @@ sparc_cons (expressionS *exp, int size)
              sparc_cons_special_reloc = "plt";
            }
        }
-      else if (strncmp (input_line_pointer + 3, "tls_dtpoff", 10) == 0)
+      else if (startswith (input_line_pointer + 3, "tls_dtpoff"))
        {
          if (size != 4 && size != 8)
            as_bad (_("Illegal operands: %%r_tls_dtpoff in %d-byte data field"), size);
@@ -4750,8 +4851,6 @@ sparc_cons (expressionS *exp, int size)
   return sparc_cons_special_reloc;
 }
 
-#endif
-
 /* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
    reloc for a cons.  We could use the definition there, except that
    we want to handle little endian relocs specially.  */
@@ -4774,6 +4873,15 @@ cons_fix_new_sparc (fragS *frag,
       && now_seg->flags & SEC_ALLOC)
     r = BFD_RELOC_SPARC_REV32;
 
+#ifdef TE_SOLARIS
+  /* The Solaris linker does not allow R_SPARC_UA64
+     relocations for 32-bit executables.  */
+  if (!target_little_endian_data
+      && sparc_arch_size != 64
+      && r == BFD_RELOC_64)
+    r = BFD_RELOC_32;
+#endif
+
   if (sparc_cons_special_reloc)
     {
       if (*sparc_cons_special_reloc == 'd')
@@ -4798,13 +4906,23 @@ cons_fix_new_sparc (fragS *frag,
          case 8: r = BFD_RELOC_SPARC_TLS_DTPOFF64; break;
          }
     }
-  else if (sparc_no_align_cons)
+  else if (sparc_no_align_cons
+          || /* PR 20803 - relocs in the .eh_frame section
+                need to support unaligned access.  */
+          strcmp (now_seg->name, ".eh_frame") == 0)
     {
       switch (nbytes)
        {
        case 2: r = BFD_RELOC_SPARC_UA16; break;
        case 4: r = BFD_RELOC_SPARC_UA32; break;
+#ifdef TE_SOLARIS
+        /* The Solaris linker does not allow R_SPARC_UA64
+          relocations for 32-bit executables.  */
+        case 8: r = sparc_arch_size == 64 ?
+                    BFD_RELOC_SPARC_UA64 : BFD_RELOC_SPARC_UA32; break;
+#else
        case 8: r = BFD_RELOC_SPARC_UA64; break;
+#endif
        default: abort ();
        }
    }
@@ -4821,18 +4939,25 @@ sparc_cfi_frame_initial_instructions (void)
 int
 sparc_regname_to_dw2regnum (char *regname)
 {
-  char *p, *q;
+  char *q;
+  int i;
 
   if (!regname[0])
     return -1;
 
-  q = "goli";
-  p = strchr (q, regname[0]);
-  if (p)
+  switch (regname[0])
+    {
+    case 'g': i = 0; break;
+    case 'o': i = 1; break;
+    case 'l': i = 2; break;
+    case 'i': i = 3; break;
+    default: i = -1; break;
+    }
+  if (i != -1)
     {
       if (regname[1] < '0' || regname[1] > '8' || regname[2])
        return -1;
-      return (p - q) * 8 + regname[1] - '0';
+      return i * 8 + regname[1] - '0';
     }
   if (regname[0] == 's' && regname[1] == 'p' && !regname[2])
     return 14;
@@ -4843,7 +4968,7 @@ sparc_regname_to_dw2regnum (char *regname)
       unsigned int regnum;
 
       regnum = strtoul (regname + 1, &q, 10);
-      if (p == q || *q)
+      if (q == NULL || *q)
         return -1;
       if (regnum >= ((regname[0] == 'f'
                      && SPARC_OPCODE_ARCH_V9_P (max_architecture))