Fix copyright notices
[deliverable/binutils-gdb.git] / gas / config / tc-sparc.c
index 733e3d22a1879097833f3f208a9734b6710ce6bf..dc90f5d79d682ce4139ce66674de8c4f45fa9e48 100644 (file)
@@ -1,6 +1,7 @@
 /* tc-sparc.c -- Assemble for the SPARC
-   Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
-
+   Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+   1999, 2000, 2001
+   Free Software Foundation, Inc.
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
-   along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-#define cypress 1234
+   You should have received a copy of the GNU General Public
+   License along with GAS; see the file COPYING.  If not, write
+   to the Free Software Foundation, 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
 
 #include <stdio.h>
 #include <ctype.h>
 #include "as.h"
 #include "subsegs.h"
 
-/* careful, this file includes data *declarations* */
 #include "opcode/sparc.h"
 
-static void sparc_ip PARAMS ((char *));
+#ifdef OBJ_ELF
+#include "elf/sparc.h"
+#include "dwarf2dbg.h"
+#endif
+
+static struct sparc_arch *lookup_arch PARAMS ((char *));
+static void init_default_arch PARAMS ((void));
+static int sparc_ip PARAMS ((char *, const struct sparc_opcode **));
+static int in_signed_range PARAMS ((bfd_signed_vma, bfd_signed_vma));
+static int in_unsigned_range PARAMS ((bfd_vma, bfd_vma));
+static int in_bitfield_range PARAMS ((bfd_signed_vma, bfd_signed_vma));
+static int sparc_ffs PARAMS ((unsigned int));
+static void synthetize_setuw PARAMS ((const struct sparc_opcode *));
+static void synthetize_setsw PARAMS ((const struct sparc_opcode *));
+static void synthetize_setx PARAMS ((const struct sparc_opcode *));
+static bfd_vma BSR PARAMS ((bfd_vma, int));
+static int cmp_reg_entry PARAMS ((const PTR, const PTR));
+static int parse_keyword_arg PARAMS ((int (*) (const char *), char **, int *));
+static int parse_const_expr_arg PARAMS ((char **, int *));
+static int get_expression PARAMS ((char *str));
+
+/* Default architecture.  */
+/* ??? The default value should be V8, but sparclite support was added
+   by making it the default.  GCC now passes -Asparclite, so maybe sometime in
+   the future we can set this to V8.  */
+#ifndef DEFAULT_ARCH
+#define DEFAULT_ARCH "sparclite"
+#endif
+static char *default_arch = DEFAULT_ARCH;
+
+/* Non-zero if the initial values of `max_architecture' and `sparc_arch_size'
+   have been set.  */
+static int default_init_p;
+
+/* Current architecture.  We don't bump up unless necessary.  */
+static enum sparc_opcode_arch_val current_architecture = SPARC_OPCODE_ARCH_V6;
+
+/* The maximum architecture level we can bump up to.
+   In a 32 bit environment, don't allow bumping up to v9 by default.
+   The native assembler works this way.  The user is required to pass
+   an explicit argument before we'll create v9 object files.  However, if
+   we don't see any v9 insns, a v8plus object file is not created.  */
+static enum sparc_opcode_arch_val max_architecture;
+
+/* Either 32 or 64, selects file format.  */
+static int sparc_arch_size;
+/* Initial (default) value, recorded separately in case a user option
+   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;
+#endif
 
-static enum sparc_architecture current_architecture = v6;
 static int architecture_requested;
 static int warn_on_bump;
 
+/* If warn_on_bump and the needed architecture is higher than this
+   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.  */
+static int no_undeclared_regs;
+
+/* Non-zero if we should try to relax jumps and calls.  */
+static int sparc_relax;
+
+/* Non-zero if we are generating PIC code.  */
+int sparc_pic_code;
+
+/* Non-zero if we should give an error when misaligned data is seen.  */
+static int enforce_aligned_data;
+
 extern int target_big_endian;
 
-const relax_typeS md_relax_table[1];
+static int target_little_endian_data;
+
+/* Symbols for global registers on v9.  */
+static symbolS *globals[8];
+
+/* V9 and 86x have big and little endian data, but instructions are always big
+   endian.  The sparclet has bi-endian support but both data and insns have
+   the same endianness.  Global `target_big_endian' is used for data.
+   The following macro is used for instructions.  */
+#ifndef INSN_BIG_ENDIAN
+#define INSN_BIG_ENDIAN (target_big_endian \
+                        || default_arch_type == sparc86x \
+                        || SPARC_OPCODE_ARCH_V9_P (max_architecture))
+#endif
 
-/* handle of the OPCODE hash table */
-static struct hash_control *op_hash = NULL;
+/* Handle of the OPCODE hash table.  */
+static struct hash_control *op_hash;
 
+static int log2 PARAMS ((int));
 static void s_data1 PARAMS ((void));
 static void s_seg PARAMS ((int));
 static void s_proc PARAMS ((int));
 static void s_reserve PARAMS ((int));
 static void s_common PARAMS ((int));
+static void s_empty PARAMS ((int));
+static void s_uacons PARAMS ((int));
+static void s_ncons PARAMS ((int));
+static void s_register PARAMS ((int));
 
 const pseudo_typeS md_pseudo_table[] =
 {
-  {"align", s_align_bytes, 0}, /* Defaulting is invalid (0) */
+  {"align", s_align_bytes, 0}, /* Defaulting is invalid (0) */
   {"common", s_common, 0},
+  {"empty", s_empty, 0},
   {"global", s_globl, 0},
   {"half", cons, 2},
+  {"nword", s_ncons, 0},
   {"optim", s_ignore, 0},
   {"proc", s_proc, 0},
   {"reserve", s_reserve, 0},
   {"seg", s_seg, 0},
   {"skip", s_space, 0},
   {"word", cons, 4},
-/* start-sanitize-v9 */
-#ifndef NO_V9
   {"xword", cons, 8},
+  {"uahalf", s_uacons, 2},
+  {"uaword", s_uacons, 4},
+  {"uaxword", s_uacons, 8},
 #ifdef OBJ_ELF
-  {"uaxword", cons, 8},
-#endif
-#endif
-/* end-sanitize-v9 */
-#ifdef OBJ_ELF
-  /* these are specific to sparc/svr4 */
-  {"pushsection", obj_elf_section, 0},
-  {"popsection", obj_elf_previous, 0},
-  {"uaword", cons, 4},
-  {"uahalf", cons, 2},
+  {"file", dwarf2_directive_file, 0},
+  {"loc", dwarf2_directive_loc, 0},
+  /* 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},
 };
 
-const int md_short_jump_size = 4;
-const int md_long_jump_size = 4;
-const int md_reloc_size = 12;  /* Size of relocation record */
+/* Size of relocation record.  */
+const int md_reloc_size = 12;
 
 /* This array holds the chars that always start a comment.  If the
-   pre-processor is disabled, these aren't very useful */
-const char comment_chars[] = "!";      /* JF removed '|' from comment_chars */
+   pre-processor is disabled, these aren't very useful.  */
+const char comment_chars[] = "!";      /* JF removed '|' from
+                                           comment_chars.  */
 
 /* This array holds the chars that only start a comment at the beginning of
    a line.  If the line seems to have the form '# 123 filename'
-   .line and .file directives will appear in the pre-processed output */
+   .line and .file directives will appear in the pre-processed output */
 /* Note that input_file.c hand checks for '#' at the beginning of the
    first line of the input file.  This is because the compiler outputs
-   #NO_APP at the beginning of its output. */
+   #NO_APP at the beginning of its output.  */
 /* Also note that comments started like this one will always
-   work if '/' isn't otherwise defined. */
+   work if '/' isn't otherwise defined.  */
 const char line_comment_chars[] = "#";
 
-const char line_separator_chars[] = "";
+const char line_separator_chars[] = ";";
 
-/* Chars that can be used to separate mant from exp in floating point nums */
+/* Chars that can be used to separate mant from exp in floating point
+   nums.  */
 const char EXP_CHARS[] = "eE";
 
-/* Chars that mean this number is a floating point constant */
-/* As in 0f12.456 */
-/* or    0d1.2345e12 */
+/* Chars that mean this number is a floating point constant.
+   As in 0f12.456
+   or    0d1.2345e12  */
 const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 /* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
    changed in read.c.  Ideally it shouldn't have to know about it at all,
    but nothing is ideal around here.  */
 
-static unsigned char octal[256];
-#define isoctal(c)  octal[(unsigned char) (c)]
-static unsigned char toHex[256];
+#define isoctal(c)  ((unsigned) ((c) - '0') < '8')
 
 struct sparc_it
   {
@@ -119,395 +205,525 @@ struct sparc_it
     unsigned long opcode;
     struct nlist *nlistp;
     expressionS exp;
+    expressionS exp2;
     int pcrel;
     bfd_reloc_code_real_type reloc;
   };
 
 struct sparc_it the_insn, set_insn;
 
-static INLINE int
-in_signed_range (val, max)
-     bfd_signed_vma val, max;
+static void output_insn
+  PARAMS ((const struct sparc_opcode *, struct sparc_it *));
+\f
+/* Table of arguments to -A.
+   The sparc_opcode_arch table in sparc-opc.c is insufficient and incorrect
+   for this use.  That table is for opcodes only.  This table is for opcodes
+   and file formats.  */
+
+enum sparc_arch_types {v6, v7, v8, sparclet, sparclite, sparc86x, v8plus,
+                      v8plusa, v9, v9a, v9b, v9_64};
+
+static struct sparc_arch {
+  char *name;
+  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;
+} sparc_arch_table[] = {
+  { "v6", "v6", v6, 0, 1 },
+  { "v7", "v7", v7, 0, 1 },
+  { "v8", "v8", v8, 32, 1 },
+  { "sparclet", "sparclet", sparclet, 32, 1 },
+  { "sparclite", "sparclite", sparclite, 32, 1 },
+  { "sparc86x", "sparclite", sparc86x, 32, 1 },
+  { "v8plus", "v9", v9, 0, 1 },
+  { "v8plusa", "v9a", v9, 0, 1 },
+  { "v8plusb", "v9b", v9, 0, 1 },
+  { "v9", "v9", v9, 0, 1 },
+  { "v9a", "v9a", v9, 0, 1 },
+  { "v9b", "v9b", v9, 0, 1 },
+  /* This exists to allow configure.in/Makefile.in to pass one
+     value to specify both the default machine and default word size.  */
+  { "v9-64", "v9", v9, 64, 0 },
+  { NULL, NULL, v8, 0, 0 }
+};
+
+/* Variant of default_arch */
+static enum sparc_arch_types default_arch_type;
+
+static struct sparc_arch *
+lookup_arch (name)
+     char *name;
 {
-  if (max <= 0)
-    abort ();
-  if (val > max)
-    return 0;
-  if (val < ~max)
-    return 0;
-  return 1;
+  struct sparc_arch *sa;
+
+  for (sa = &sparc_arch_table[0]; sa->name != NULL; sa++)
+    if (strcmp (sa->name, name) == 0)
+      break;
+  if (sa->name == NULL)
+    return NULL;
+  return sa;
+}
+
+/* Initialize the default opcode arch and word size from the default
+   architecture name.  */
+
+static void
+init_default_arch ()
+{
+  struct sparc_arch *sa = lookup_arch (default_arch);
+
+  if (sa == NULL
+      || sa->default_arch_size == 0)
+    as_fatal (_("Invalid default architecture, broken assembler."));
+
+  max_architecture = sparc_opcode_lookup_arch (sa->opcode_arch);
+  if (max_architecture == SPARC_OPCODE_ARCH_BAD)
+    as_fatal (_("Bad opcode table, broken assembler."));
+  default_arch_size = sparc_arch_size = sa->default_arch_size;
+  default_init_p = 1;
+  default_arch_type = sa->arch_type;
 }
 
-#if 0
-static void print_insn PARAMS ((struct sparc_it *insn));
+/* Called by TARGET_FORMAT.  */
+
+const char *
+sparc_target_format ()
+{
+  /* 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
-static int getExpression PARAMS ((char *str));
 
-static char *expr_end;
-static int special_case;
+#ifdef OBJ_BOUT
+  return "b.out.big";
+#endif
 
-/*
- * Instructions that require wierd handling because they're longer than
- * 4 bytes.
- */
-#define        SPECIAL_CASE_SET        1
-#define        SPECIAL_CASE_FDIV       2
+#ifdef OBJ_COFF
+#ifdef TE_LYNX
+  return "coff-sparc-lynx";
+#else
+  return "coff-sparc";
+#endif
+#endif
 
-/*
- * sort of like s_lcomm
+#ifdef OBJ_ELF
+  return sparc_arch_size == 64 ? "elf64-sparc" : "elf32-sparc";
+#endif
+
+  abort ();
+}
+\f
+/* md_parse_option
+ *     Invocation line includes a switch not recognized by the base assembler.
+ *     See if it's a processor-specific option.  These are:
+ *
+ *     -bump
+ *             Warn on architecture bumps.  See also -A.
+ *
+ *     -Av6, -Av7, -Av8, -Asparclite, -Asparclet
+ *             Standard 32 bit architectures.
+ *     -Av9, -Av9a, -Av9b
+ *             Sparc64 in either a 32 or 64 bit world (-32/-64 says which).
+ *             This used to only mean 64 bits, but properly specifying it
+ *             complicated gcc's ASM_SPECs, so now opcode selection is
+ *             specified orthogonally to word size (except when specifying
+ *             the default, but that is an internal implementation detail).
+ *     -Av8plus, -Av8plusa, -Av8plusb
+ *             Same as -Av9{,a,b}.
+ *     -xarch=v8plus, -xarch=v8plusa, -xarch=v8plusb
+ *             Same as -Av8plus{,a,b} -32, for compatibility with Sun's
+ *             assembler.
+ *     -xarch=v9, -xarch=v9a, -xarch=v9b
+ *             Same as -Av9{,a,b} -64, for compatibility with Sun's
+ *             assembler.
+ *
+ *             Select the architecture and possibly the file format.
+ *             Instructions or features not supported by the selected
+ *             architecture cause fatal errors.
+ *
+ *             The default is to start at v6, and bump the architecture up
+ *             whenever an instruction is seen at a higher level.  In 32 bit
+ *             environments, v9 is not bumped up to, the user must pass
+ *             -Av8plus{,a,b}.
+ *
+ *             If -bump is specified, a warning is printing when bumping to
+ *             higher levels.
  *
+ *             If an architecture is specified, all instructions must match
+ *             that architecture.  Any higher level instructions are flagged
+ *             as errors.  Note that in the 32 bit environment specifying
+ *             -Av8plus does not automatically create a v8plus object file, a
+ *             v9 insn must be seen.
+ *
+ *             If both an architecture and -bump are specified, the
+ *             architecture starts at the specified level, but bumps are
+ *             warnings.  Note that we can't set `current_architecture' to
+ *             the requested level in this case: in the 32 bit environment,
+ *             we still must avoid creating v8plus object files unless v9
+ *             insns are seen.
+ *
+ * Note:
+ *             Bumping between incompatible architectures is always an
+ *             error.  For example, from sparclite to v9.
  */
-#ifndef OBJ_ELF
-static int max_alignment = 15;
+
+#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},
+#define OPTION_SPARC (OPTION_MD_BASE + 1)
+  {"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)
+  {"64", no_argument, NULL, OPTION_64},
+#define OPTION_TSO (OPTION_MD_BASE + 5)
+  {"TSO", no_argument, NULL, OPTION_TSO},
+#define OPTION_PSO (OPTION_MD_BASE + 6)
+  {"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},
+#define OPTION_BIG_ENDIAN (OPTION_MD_BASE + 9)
+  {"EB", no_argument, NULL, OPTION_BIG_ENDIAN},
+#endif
+#define OPTION_ENFORCE_ALIGNED_DATA (OPTION_MD_BASE + 10)
+  {"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},
+  {NULL, no_argument, NULL, 0}
+};
 
-static void
-s_reserve (ignore)
-     int ignore;
-{
-  char *name;
-  char *p;
-  char c;
-  int align;
-  int size;
-  int temp;
-  symbolS *symbolP;
+size_t md_longopts_size = sizeof (md_longopts);
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
+int
+md_parse_option (c, arg)
+     int c;
+     char *arg;
+{
+  /* We don't get a chance to initialize anything before we're called,
+     so handle that now.  */
+  if (! default_init_p)
+    init_default_arch ();
 
-  if (*input_line_pointer != ',')
+  switch (c)
     {
-      as_bad ("Expected comma after name");
-      ignore_rest_of_line ();
-      return;
-    }
+    case OPTION_BUMP:
+      warn_on_bump = 1;
+      warn_after_architecture = SPARC_OPCODE_ARCH_V6;
+      break;
 
-  ++input_line_pointer;
+    case OPTION_XARCH:
+#ifdef OBJ_ELF
+      if (strncmp (arg, "v9", 2) != 0)
+       md_parse_option (OPTION_32, NULL);
+      else
+       md_parse_option (OPTION_64, NULL);
+#endif
+      /* Fall through.  */
+
+    case 'A':
+      {
+       struct sparc_arch *sa;
+       enum sparc_opcode_arch_val opcode_arch;
+
+       sa = lookup_arch (arg);
+       if (sa == NULL
+           || ! sa->user_option_p)
+         {
+           if (c == OPTION_XARCH)
+             as_bad (_("invalid architecture -xarch=%s"), arg);
+           else
+             as_bad (_("invalid architecture -A%s"), arg);
+           return 0;
+         }
+
+       opcode_arch = sparc_opcode_lookup_arch (sa->opcode_arch);
+       if (opcode_arch == SPARC_OPCODE_ARCH_BAD)
+         as_fatal (_("Bad opcode table, broken assembler."));
+
+       max_architecture = opcode_arch;
+       architecture_requested = 1;
+      }
+      break;
 
-  if ((size = get_absolute_expression ()) < 0)
-    {
-      as_bad ("BSS length (%d.) <0! Ignored.", size);
-      ignore_rest_of_line ();
-      return;
-    }                          /* bad length */
+    case OPTION_SPARC:
+      /* Ignore -sparc, used by SunOS make default .s.o rule.  */
+      break;
 
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
-  *p = c;
+    case OPTION_ENFORCE_ALIGNED_DATA:
+      enforce_aligned_data = 1;
+      break;
 
-  if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0
-      && strncmp (input_line_pointer, ",\".bss\"", 7) != 0)
-    {
-      as_bad ("bad .reserve segment -- expected BSS segment", input_line_pointer);
-      return;
-    }
+#ifdef SPARC_BIENDIAN
+    case OPTION_LITTLE_ENDIAN:
+      target_big_endian = 0;
+      if (default_arch_type != sparclet)
+       as_fatal ("This target does not support -EL");
+      break;
+    case OPTION_LITTLE_ENDIAN_DATA:
+      target_little_endian_data = 1;
+      target_big_endian = 0;
+      if (default_arch_type != sparc86x
+         && default_arch_type != v9)
+       as_fatal ("This target does not support --little-endian-data");
+      break;
+    case OPTION_BIG_ENDIAN:
+      target_big_endian = 1;
+      break;
+#endif
 
-  if (input_line_pointer[2] == '.')
-    input_line_pointer += 7;
-  else
-    input_line_pointer += 6;
-  SKIP_WHITESPACE ();
+#ifdef OBJ_AOUT
+    case 'k':
+      sparc_pic_code = 1;
+      break;
+#endif
 
-  if (*input_line_pointer == ',')
-    {
-      ++input_line_pointer;
+#ifdef OBJ_ELF
+    case OPTION_32:
+    case OPTION_64:
+      {
+       const char **list, **l;
+
+       sparc_arch_size = c == OPTION_32 ? 32 : 64;
+       list = bfd_target_list ();
+       for (l = list; *l != NULL; l++)
+         {
+           if (sparc_arch_size == 32)
+             {
+               if (strcmp (*l, "elf32-sparc") == 0)
+                 break;
+             }
+           else
+             {
+               if (strcmp (*l, "elf64-sparc") == 0)
+                 break;
+             }
+         }
+       if (*l == NULL)
+         as_fatal (_("No compiled in support for %d bit object file format"),
+                   sparc_arch_size);
+       free (list);
+      }
+      break;
 
-      SKIP_WHITESPACE ();
-      if (*input_line_pointer == '\n')
-       {
-         as_bad ("Missing alignment");
-         return;
-       }
+    case OPTION_TSO:
+      sparc_memory_model = MM_TSO;
+      break;
 
-      align = get_absolute_expression ();
-#ifndef OBJ_ELF
-      if (align > max_alignment)
-       {
-         align = max_alignment;
-         as_warn ("Alignment too large: %d. assumed.", align);
-       }
-#endif
-      if (align < 0)
-       {
-         align = 0;
-         as_warn ("Alignment negative. 0 assumed.");
-       }
+    case OPTION_PSO:
+      sparc_memory_model = MM_PSO;
+      break;
 
-      record_alignment (bss_section, align);
+    case OPTION_RMO:
+      sparc_memory_model = MM_RMO;
+      break;
 
-      /* convert to a power of 2 alignment */
-      for (temp = 0; (align & 1) == 0; align >>= 1, ++temp);;
+    case 'V':
+      print_version_id ();
+      break;
 
-      if (align != 1)
-       {
-         as_bad ("Alignment not a power of 2");
-         ignore_rest_of_line ();
-         return;
-       }                       /* not a power of two */
+    case 'Q':
+      /* Qy - do emit .comment
+        Qn - do not emit .comment.  */
+      break;
 
-      align = temp;
-    }                          /* if has optional alignment */
-  else
-    align = 0;
+    case 's':
+      /* Use .stab instead of .stab.excl.  */
+      break;
 
-  if ((S_GET_SEGMENT (symbolP) == bss_section
-       || !S_IS_DEFINED (symbolP))
-#ifdef OBJ_AOUT
-      && S_GET_OTHER (symbolP) == 0
-      && S_GET_DESC (symbolP) == 0
-#endif
-      )
-    {
-      if (! need_pass_2)
-       {
-         char *pfrag;
-         segT current_seg = now_seg;
-         subsegT current_subseg = now_subseg;
+    case 'q':
+      /* quick -- Native assembler does fewer checks.  */
+      break;
 
-         subseg_set (bss_section, 1); /* switch to bss */
+    case 'K':
+      if (strcmp (arg, "PIC") != 0)
+       as_warn (_("Unrecognized option following -K"));
+      else
+       sparc_pic_code = 1;
+      break;
 
-         if (align)
-           frag_align (align, 0); /* do alignment */
+    case OPTION_NO_UNDECLARED_REGS:
+      no_undeclared_regs = 1;
+      break;
 
-         /* detach from old frag */
-         if (S_GET_SEGMENT(symbolP) == bss_section)
-           symbolP->sy_frag->fr_symbol = NULL;
+    case OPTION_UNDECLARED_REGS:
+      no_undeclared_regs = 0;
+      break;
+#endif
 
-         symbolP->sy_frag = frag_now;
-         pfrag = frag_var (rs_org, 1, 1, (relax_substateT)0, symbolP,
-                           size, (char *)0);
-         *pfrag = 0;
+    case OPTION_RELAX:
+      sparc_relax = 1;
+      break;
 
-         S_SET_SEGMENT (symbolP, bss_section);
+    case OPTION_NO_RELAX:
+      sparc_relax = 0;
+      break;
 
-         subseg_set (current_seg, current_subseg);
-       }
+    default:
+      return 0;
     }
-  else
-    {
-      as_warn("Ignoring attempt to re-define symbol %s.", name);
-    }                          /* if not redefining */
 
-  demand_empty_rest_of_line ();
+  return 1;
 }
 
-static void
-s_common (ignore)
-     int ignore;
+void
+md_show_usage (stream)
+     FILE *stream;
 {
-  char *name;
-  char c;
-  char *p;
-  int temp, size;
-  symbolS *symbolP;
+  const struct sparc_arch *arch;
+  int column;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  /* just after name is now '\0' */
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
-  if (*input_line_pointer != ',')
+  /* We don't get a chance to initialize anything before we're called,
+     so handle that now.  */
+  if (! default_init_p)
+    init_default_arch ();
+
+  fprintf (stream, _("SPARC options:\n"));
+  column = 0;
+  for (arch = &sparc_arch_table[0]; arch->name; arch++)
     {
-      as_bad ("Expected comma after symbol-name");
-      ignore_rest_of_line ();
-      return;
+      if (!arch->user_option_p)
+       continue;
+      if (arch != &sparc_arch_table[0])
+       fprintf (stream, " | ");
+      if (column + strlen(arch->name) > 70)
+       {
+         column = 0;
+         fputc ('\n', stream);
+       }
+      column += 5 + 2 + strlen(arch->name);
+      fprintf (stream, "-A%s", arch->name);
     }
-  input_line_pointer++;                /* skip ',' */
-  if ((temp = get_absolute_expression ()) < 0)
+  for (arch = &sparc_arch_table[0]; arch->name; arch++)
     {
-      as_bad (".COMMon length (%d.) <0! Ignored.", temp);
-      ignore_rest_of_line ();
-      return;
+      if (!arch->user_option_p)
+       continue;
+      fprintf (stream, " | ");
+      if (column + strlen(arch->name) > 65)
+       {
+         column = 0;
+         fputc ('\n', stream);
+       }
+      column += 5 + 7 + strlen(arch->name);
+      fprintf (stream, "-xarch=%s", arch->name);
     }
-  size = temp;
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
-  *p = c;
-  if (S_IS_DEFINED (symbolP))
-    {
-      as_bad ("Ignoring attempt to re-define symbol");
-      ignore_rest_of_line ();
-      return;
-    }
-  if (S_GET_VALUE (symbolP) != 0)
-    {
-      if (S_GET_VALUE (symbolP) != size)
-       {
-         as_warn ("Length of .comm \"%s\" is already %ld. Not changed to %d.",
-                  S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
-       }
-    }
-  else
-    {
-#ifndef OBJ_ELF
-      S_SET_VALUE (symbolP, (valueT) size);
-      S_SET_EXTERNAL (symbolP);
-#endif
-    }
-  know (symbolP->sy_frag == &zero_address_frag);
-  if (*input_line_pointer != ',')
-    {
-      as_bad ("Expected comma after common length");
-      ignore_rest_of_line ();
-      return;
-    }
-  input_line_pointer++;
-  SKIP_WHITESPACE ();
-  if (*input_line_pointer != '"')
-    {
-      temp = get_absolute_expression ();
-#ifndef OBJ_ELF
-      if (temp > max_alignment)
-       {
-         temp = max_alignment;
-         as_warn ("Common alignment too large: %d. assumed", temp);
-       }
+  fprintf (stream, _("\n\
+                       specify variant of SPARC architecture\n\
+-bump                  warn when assembler switches architectures\n\
+-sparc                 ignored\n\
+--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
-      if (temp < 0)
-       {
-         temp = 0;
-         as_warn ("Common alignment negative; 0 assumed");
-       }
 #ifdef OBJ_ELF
-      if (symbolP->local)
-       {
-         segT old_sec;
-         int old_subsec;
-         char *p;
-         int align;
-
-       allocate_bss:
-         old_sec = now_seg;
-         old_subsec = now_subseg;
-         align = temp;
-         record_alignment (bss_section, align);
-         subseg_set (bss_section, 0);
-         if (align)
-           frag_align (align, 0);
-         if (S_GET_SEGMENT (symbolP) == bss_section)
-           symbolP->sy_frag->fr_symbol = 0;
-         symbolP->sy_frag = frag_now;
-         p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
-                       (char *) 0);
-         *p = 0;
-         S_SET_SEGMENT (symbolP, bss_section);
-         S_CLEAR_EXTERNAL (symbolP);
-         subseg_set (old_sec, old_subsec);
-       }
-      else
+  fprintf (stream, _("\
+-32                    create 32 bit object file\n\
+-64                    create 64 bit object file\n"));
+  fprintf (stream, _("\
+                       [default is %d]\n"), default_arch_size);
+  fprintf (stream, _("\
+-TSO                   use Total Store Ordering\n\
+-PSO                   use Partial Store Ordering\n\
+-RMO                   use Relaxed Memory Ordering\n"));
+  fprintf (stream, _("\
+                       [default is %s]\n"), (default_arch_size == 64) ? "RMO" : "TSO");
+  fprintf (stream, _("\
+-KPIC                  generate PIC\n\
+-V                     print assembler version number\n\
+-undeclared-regs       ignore application global register usage without\n\
+                       appropriate .register directive (default)\n\
+-no-undeclared-regs    force error on application global register usage\n\
+                       without appropriate .register directive\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\
+-EB                    generate code for a big endian machine\n\
+--little-endian-data   generate code for a machine having big endian\n\
+                        instructions and little endian data.\n"));
 #endif
-       {
-       allocate_common:
-         S_SET_VALUE (symbolP, (valueT) size);
-         S_SET_EXTERNAL (symbolP);
-         /* should be common, but this is how gas does it for now */
-         S_SET_SEGMENT (symbolP, &bfd_und_section);
-       }
-    }
-  else
-    {
-      input_line_pointer++;
-      /* @@ Some use the dot, some don't.  Can we get some consistency??  */
-      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))
-       {
-         while (*--input_line_pointer != '"')
-           ;
-         input_line_pointer--;
-         goto bad_common_segment;
-       }
-      while (*input_line_pointer++ != '"')
-       ;
-      goto allocate_common;
-    }
-  demand_empty_rest_of_line ();
-  return;
-
-  {
-  bad_common_segment:
-    p = input_line_pointer;
-    while (*p && *p != '\n')
-      p++;
-    c = *p;
-    *p = '\0';
-    as_bad ("bad .common segment %s", input_line_pointer + 1);
-    *p = c;
-    input_line_pointer = p;
-    ignore_rest_of_line ();
-    return;
-  }
-}
-
-static void
-s_seg (ignore)
-     int ignore;
-{
-
-  if (strncmp (input_line_pointer, "\"text\"", 6) == 0)
-    {
-      input_line_pointer += 6;
-      s_text (0);
-      return;
-    }
-  if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
-    {
-      input_line_pointer += 6;
-      s_data (0);
-      return;
-    }
-  if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
-    {
-      input_line_pointer += 7;
-      s_data1 ();
-      return;
-    }
-  if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
-    {
-      input_line_pointer += 5;
-      /* We only support 2 segments -- text and data -- for now, so
-        things in the "bss segment" will have to go into data for now.
-        You can still allocate SEG_BSS stuff with .lcomm or .reserve. */
-      subseg_set (data_section, 255);  /* FIXME-SOMEDAY */
-      return;
-    }
-  as_bad ("Unknown segment type");
-  demand_empty_rest_of_line ();
-}
-
-static void
-s_data1 ()
-{
-  subseg_set (data_section, 1);
-  demand_empty_rest_of_line ();
 }
-
-static void
-s_proc (ignore)
-     int ignore;
+\f
+/* Native operand size opcode translation.  */
+struct
+  {
+    char *name;
+    char *name32;
+    char *name64;
+  } native_op_table[] =
 {
-  while (!is_end_of_line[(unsigned char) *input_line_pointer])
-    {
-      ++input_line_pointer;
-    }
-  ++input_line_pointer;
-}
-
-/* start-sanitize-v9 */
-#ifndef NO_V9
+  {"ldn", "ld", "ldx"},
+  {"ldna", "lda", "ldxa"},
+  {"stn", "st", "stx"},
+  {"stna", "sta", "stxa"},
+  {"slln", "sll", "sllx"},
+  {"srln", "srl", "srlx"},
+  {"sran", "sra", "srax"},
+  {"casn", "cas", "casx"},
+  {"casna", "casa", "casxa"},
+  {"clrn", "clr", "clrx"},
+  {NULL, NULL, NULL},
+};
+\f
+/* sparc64 priviledged registers.  */
 
 struct priv_reg_entry
-  {
-    char *name;
-    int regnum;
-  };
+{
+  char *name;
+  int regnum;
+};
 
 struct priv_reg_entry priv_reg_table[] =
 {
@@ -528,41 +744,41 @@ struct priv_reg_entry priv_reg_table[] =
   {"wstate", 14},
   {"fq", 15},
   {"ver", 31},
-  {"", -1},                    /* end marker */
-};
-
-struct membar_masks
-{
-  char *name;
-  unsigned int len;
-  unsigned int mask;
+  {"", -1},                    /* End marker.  */
 };
 
-#define MEMBAR_MASKS_SIZE 7
+/* v9a specific asrs.  */
 
-struct membar_masks membar_masks[MEMBAR_MASKS_SIZE] =
+struct priv_reg_entry v9a_asr_table[] =
 {
-  {"Sync", 4, 0x40},
-  {"MemIssue", 8, 0x20},
-  {"Lookaside", 9, 0x10},
-  {"StoreStore", 10, 0x08},
-  {"LoadStore", 9, 0x04},
-  {"StoreLoad", 9, 0x02},
-  {"LoadLoad", 8, 0x01},
+  {"tick_cmpr", 23},
+  {"sys_tick_cmpr", 25},
+  {"sys_tick", 24},
+  {"softint", 22},
+  {"set_softint", 20},
+  {"pic", 17},
+  {"pcr", 16},
+  {"gsr", 19},
+  {"dcr", 18},
+  {"clear_softint", 21},
+  {"", -1},                    /* End marker.  */
 };
 
 static int
-cmp_reg_entry (p, q)
-     struct priv_reg_entry *p, *q;
+cmp_reg_entry (parg, qarg)
+     const PTR parg;
+     const PTR 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);
 }
-
-#endif
-/* end-sanitize-v9 */
-
+\f
 /* This function is called once, at assembler startup time.  It should
-   set up all the tables, etc. that the MD part of the assembler will need. */
+   set up all the tables, etc. that the MD part of the assembler will
+   need.  */
+
 void
 md_begin ()
 {
@@ -570,316 +786,753 @@ md_begin ()
   int lose = 0;
   register 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
+     now if not already done.  */
+  if (! default_init_p)
+    init_default_arch ();
+
   op_hash = hash_new ();
 
-  while (i < NUMOPCODES)
+  while (i < (unsigned int) sparc_num_opcodes)
     {
       const char *name = sparc_opcodes[i].name;
-      retval = hash_insert (op_hash, name, &sparc_opcodes[i]);
+      retval = hash_insert (op_hash, name, (PTR) &sparc_opcodes[i]);
       if (retval != NULL)
        {
-         fprintf (stderr, "internal error: can't hash `%s': %s\n",
-                  sparc_opcodes[i].name, retval);
+         as_bad (_("Internal error: can't hash `%s': %s\n"),
+                 sparc_opcodes[i].name, retval);
          lose = 1;
        }
       do
        {
          if (sparc_opcodes[i].match & sparc_opcodes[i].lose)
            {
-             fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n",
-                      sparc_opcodes[i].name, sparc_opcodes[i].args);
+             as_bad (_("Internal error: losing opcode: `%s' \"%s\"\n"),
+                     sparc_opcodes[i].name, sparc_opcodes[i].args);
              lose = 1;
            }
          ++i;
        }
-      while (i < NUMOPCODES
+      while (i < (unsigned int) sparc_num_opcodes
             && !strcmp (sparc_opcodes[i].name, name));
     }
 
+  for (i = 0; native_op_table[i].name; i++)
+    {
+      const struct sparc_opcode *insn;
+      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);
+      if (insn == NULL)
+       {
+         as_bad (_("Internal error: can't find opcode `%s' for `%s'\n"),
+                 name, native_op_table[i].name);
+         lose = 1;
+       }
+      else
+       {
+         retval = hash_insert (op_hash, native_op_table[i].name, (PTR) insn);
+         if (retval != NULL)
+           {
+             as_bad (_("Internal error: can't hash `%s': %s\n"),
+                     sparc_opcodes[i].name, retval);
+             lose = 1;
+           }
+       }
+    }
+
   if (lose)
-    as_fatal ("Broken assembler.  No assembly attempted.");
-
-  for (i = '0'; i < '8'; ++i)
-    octal[i] = 1;
-  for (i = '0'; i <= '9'; ++i)
-    toHex[i] = i - '0';
-  for (i = 'a'; i <= 'f'; ++i)
-    toHex[i] = i + 10 - 'a';
-  for (i = 'A'; i <= 'F'; ++i)
-    toHex[i] = i + 10 - 'A';
-
-  /* start-sanitize-v9 */
-#ifndef NO_V9
-#ifdef sparcv9
-  current_architecture = v9;
-#endif
+    as_fatal (_("Broken assembler.  No assembly attempted."));
 
   qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]),
         sizeof (priv_reg_table[0]), cmp_reg_entry);
-#endif
-  /* end-sanitize-v9 */
 
-  target_big_endian = 1;
+  /* 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
+     for all upwards bumps.  If it was, we don't start issuing warnings until
+     we need to bump beyond the requested architecture or when we bump between
+     conflicting architectures.  */
+
+  if (warn_on_bump
+      && architecture_requested)
+    {
+      /* `max_architecture' records the requested architecture.
+        Issue warnings if we go above it.  */
+      warn_after_architecture = max_architecture;
+
+      /* Find the highest architecture level that doesn't conflict with
+        the requested one.  */
+      for (max_architecture = SPARC_OPCODE_ARCH_MAX;
+          max_architecture > warn_after_architecture;
+          --max_architecture)
+       if (! SPARC_OPCODE_CONFLICT_P (max_architecture,
+                                      warn_after_architecture))
+         break;
+    }
 }
 
+/* Called after all assembly has been done.  */
+
 void
-md_assemble (str)
-     char *str;
+sparc_md_end ()
 {
-  char *toP;
-  int rsd;
+  unsigned long mach = bfd_mach_sparc;
+
+  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;
+      default: mach = bfd_mach_sparc_v9; break;
+      }
+  else
+    switch (current_architecture)
+      {
+      case SPARC_OPCODE_ARCH_SPARCLET: mach = bfd_mach_sparc_sparclet; break;
+      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;
+      /* 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;
+      }
+  bfd_set_arch_mach (stdoutput, bfd_arch_sparc, mach);
+}
+\f
+/* Return non-zero if VAL is in the range -(MAX+1) to MAX.  */
 
-  know (str);
-  sparc_ip (str);
+static INLINE int
+in_signed_range (val, max)
+     bfd_signed_vma val, max;
+{
+  if (max <= 0)
+    abort ();
+  /* Sign-extend the value from the architecture word size, so that
+     0xffffffff is always considered -1 on sparc32.  */
+  if (sparc_arch_size == 32)
+    {
+      bfd_signed_vma sign = (bfd_signed_vma) 1 << 31;
+      val = ((val & 0xffffffff) ^ sign) - sign;
+    }
+  if (val > max)
+    return 0;
+  if (val < ~max)
+    return 0;
+  return 1;
+}
+
+/* Return non-zero if VAL is in the range 0 to MAX.  */
+
+static INLINE int
+in_unsigned_range (val, max)
+     bfd_vma val, max;
+{
+  if (val > max)
+    return 0;
+  return 1;
+}
+
+/* Return non-zero if VAL is in the range -(MAX/2+1) to MAX.
+   (e.g. -15 to +31).  */
+
+static INLINE int
+in_bitfield_range (val, max)
+     bfd_signed_vma val, max;
+{
+  if (max <= 0)
+    abort ();
+  if (val > max)
+    return 0;
+  if (val < ~(max >> 1))
+    return 0;
+  return 1;
+}
+
+static int
+sparc_ffs (mask)
+     unsigned int mask;
+{
+  int i;
+
+  if (mask == 0)
+    return -1;
+
+  for (i = 0; (mask & 1) == 0; ++i)
+    mask >>= 1;
+  return i;
+}
+
+/* Implement big shift right.  */
+static bfd_vma
+BSR (val, amount)
+     bfd_vma val;
+     int amount;
+{
+  if (sizeof (bfd_vma) <= 4 && amount >= 32)
+    as_fatal (_("Support for 64-bit arithmetic not compiled in."));
+  return val >> amount;
+}
+\f
+/* For communication between sparc_ip and get_expression.  */
+static char *expr_end;
+
+/* Values for `special_case'.
+   Instructions that require wierd handling because they're longer than
+   4 bytes.  */
+#define SPECIAL_CASE_NONE      0
+#define        SPECIAL_CASE_SET        1
+#define SPECIAL_CASE_SETSW     2
+#define SPECIAL_CASE_SETX      3
+/* FIXME: sparc-opc.c doesn't have necessary "S" trigger to enable this.  */
+#define        SPECIAL_CASE_FDIV       4
+
+/* Bit masks of various insns.  */
+#define NOP_INSN 0x01000000
+#define OR_INSN 0x80100000
+#define XOR_INSN 0x80180000
+#define FMOVS_INSN 0x81A00020
+#define SETHI_INSN 0x01000000
+#define SLLX_INSN 0x81281000
+#define SRA_INSN 0x81380000
+
+/* The last instruction to be assembled.  */
+static const struct sparc_opcode *last_insn;
+/* The assembled opcode of `last_insn'.  */
+static unsigned long last_opcode;
+\f
+/* Handle the set and setuw synthetic instructions.  */
+
+static void
+synthetize_setuw (insn)
+     const struct sparc_opcode *insn;
+{
+  int need_hi22_p = 0;
+  int rd = (the_insn.opcode & RD (~0)) >> 25;
 
-  /* See if "set" operand is absolute and small; skip sethi if so. */
-  if (special_case == SPECIAL_CASE_SET
-      && the_insn.exp.X_op == O_constant)
+  if (the_insn.exp.X_op == O_constant)
     {
-      if (the_insn.exp.X_add_number >= -(1 << 12)
-         && the_insn.exp.X_add_number < (1 << 12))
+      if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+       {
+         if (sizeof (offsetT) > 4
+             && (the_insn.exp.X_add_number < 0
+                 || the_insn.exp.X_add_number > (offsetT) 0xffffffff))
+           as_warn (_("set: number not in 0..4294967295 range"));
+       }
+      else
        {
-         the_insn.opcode = 0x80102000  /* or %g0,imm,... */
-           | (the_insn.opcode & 0x3E000000)    /* dest reg */
-           | (the_insn.exp.X_add_number & 0x1FFF);     /* imm */
-         special_case = 0;     /* No longer special */
-         the_insn.reloc = BFD_RELOC_NONE;      /* No longer relocated */
+         if (sizeof (offsetT) > 4
+             && (the_insn.exp.X_add_number < -(offsetT) 0x80000000
+                 || the_insn.exp.X_add_number > (offsetT) 0xffffffff))
+           as_warn (_("set: number not in -2147483648..4294967295 range"));
+         the_insn.exp.X_add_number = (int) the_insn.exp.X_add_number;
        }
     }
 
-  toP = frag_more (4);
-  /* put out the opcode */
-  md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
+  /* See if operand is absolute and small; skip sethi if so.  */
+  if (the_insn.exp.X_op != O_constant
+      || the_insn.exp.X_add_number >= (1 << 12)
+      || the_insn.exp.X_add_number < -(1 << 12))
+    {
+      the_insn.opcode = (SETHI_INSN | RD (rd)
+                        | ((the_insn.exp.X_add_number >> 10)
+                           & (the_insn.exp.X_op == O_constant
+                              ? 0x3fffff : 0)));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_HI22 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+      need_hi22_p = 1;
+    }
 
-  /* put out the symbol-dependent stuff */
-  if (the_insn.reloc != BFD_RELOC_NONE)
+  /* See if operand has no low-order bits; skip OR if so.  */
+  if (the_insn.exp.X_op != O_constant
+      || (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0)
+      || ! need_hi22_p)
     {
-      fix_new_exp (frag_now,   /* which frag */
-                  (toP - frag_now->fr_literal),        /* where */
-                  4,           /* size */
-                  &the_insn.exp,
-                  the_insn.pcrel,
-                  the_insn.reloc);
+      the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0)
+                        | RD (rd) | IMMED
+                        | (the_insn.exp.X_add_number
+                           & (the_insn.exp.X_op != O_constant
+                              ? 0 : need_hi22_p ? 0x3ff : 0x1fff)));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_LO10 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
     }
+}
 
-  switch (special_case)
+/* Handle the setsw synthetic instruction.  */
+
+static void
+synthetize_setsw (insn)
+     const struct sparc_opcode *insn;
+{
+  int low32, rd, opc;
+
+  rd = (the_insn.opcode & RD (~0)) >> 25;
+
+  if (the_insn.exp.X_op != O_constant)
     {
-    case SPECIAL_CASE_SET:
-      special_case = 0;
-      assert (the_insn.reloc == BFD_RELOC_HI22);
-      /* See if "set" operand has no low-order bits; skip OR if so. */
-      if (the_insn.exp.X_op == O_constant
-         && ((the_insn.exp.X_add_number & 0x3FF) == 0))
-       return;
-      toP = frag_more (4);
-      rsd = (the_insn.opcode >> 25) & 0x1f;
-      the_insn.opcode = 0x80102000 | (rsd << 25) | (rsd << 14);
-      md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
-      fix_new_exp (frag_now,   /* which frag */
-                  (toP - frag_now->fr_literal),        /* where */
-                  4,           /* size */
-                  &the_insn.exp,
-                  the_insn.pcrel,
-                  BFD_RELOC_LO10);
-      return;
+      synthetize_setuw (insn);
 
-    case SPECIAL_CASE_FDIV:
-      /* According to information leaked from Sun, the "fdiv" instructions
-        on early SPARC machines would produce incorrect results sometimes.
-        The workaround is to add an fmovs of the destination register to
-        itself just after the instruction.  This was true on machines
-        with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
-      special_case = 0;
-      assert (the_insn.reloc == BFD_RELOC_NONE);
-      toP = frag_more (4);
-      rsd = (the_insn.opcode >> 25) & 0x1f;
-      the_insn.opcode = 0x81A00020 | (rsd << 25) | rsd;        /* fmovs dest,dest */
-      md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
+      /* Need to sign extend it.  */
+      the_insn.opcode = (SRA_INSN | RS1 (rd) | RD (rd));
+      the_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &the_insn);
       return;
+    }
 
-    case 0:
-      return;
+  if (sizeof (offsetT) > 4
+      && (the_insn.exp.X_add_number < -(offsetT) 0x80000000
+         || the_insn.exp.X_add_number > (offsetT) 0xffffffff))
+    as_warn (_("setsw: number not in -2147483648..4294967295 range"));
 
-    default:
-      as_fatal ("failed sanity check.");
-    }
-}
+  low32 = the_insn.exp.X_add_number;
+
+  if (low32 >= 0)
+    {
+      synthetize_setuw (insn);
+      return;
+    }
+
+  opc = OR_INSN;
+
+  the_insn.reloc = BFD_RELOC_NONE;
+  /* See if operand is absolute and small; skip sethi if so.  */
+  if (low32 < -(1 << 12))
+    {
+      the_insn.opcode = (SETHI_INSN | RD (rd)
+                        | (((~the_insn.exp.X_add_number) >> 10) & 0x3fffff));
+      output_insn (insn, &the_insn);
+      low32 = 0x1c00 | (low32 & 0x3ff);
+      opc = RS1 (rd) | XOR_INSN;
+    }
+
+  the_insn.opcode = (opc | RD (rd) | IMMED
+                    | (low32 & 0x1fff));
+  output_insn (insn, &the_insn);
+}
+
+/* Handle the setsw synthetic instruction.  */
 
 static void
-sparc_ip (str)
+synthetize_setx (insn)
+     const struct sparc_opcode *insn;
+{
+  int upper32, lower32;
+  int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14;
+  int dstreg = (the_insn.opcode & RD (~0)) >> 25;
+  int upper_dstreg;
+  int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0;
+  int need_xor10_p = 0;
+
+#define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000)
+  lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
+  upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
+#undef SIGNEXT32
+
+  upper_dstreg = tmpreg;
+  /* The tmp reg should not be the dst reg.  */
+  if (tmpreg == dstreg)
+    as_warn (_("setx: temporary register same as destination register"));
+
+  /* ??? Obviously there are other optimizations we can do
+     (e.g. sethi+shift for 0x1f0000000) and perhaps we shouldn't be
+     doing some of these.  Later.  If you do change things, try to
+     change all of this to be table driven as well.  */
+  /* What to output depends on the number if it's constant.
+     Compute that first, then output what we've decided upon.  */
+  if (the_insn.exp.X_op != O_constant)
+    {
+      if (sparc_arch_size == 32)
+       {
+         /* When arch size is 32, we want setx to be equivalent
+            to setuw for anything but constants.  */
+         the_insn.exp.X_add_number &= 0xffffffff;
+         synthetize_setuw (insn);
+         return;
+       }
+      need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
+      lower32 = 0;
+      upper32 = 0;
+    }
+  else
+    {
+      /* Reset X_add_number, we've extracted it as upper32/lower32.
+        Otherwise fixup_segment will complain about not being able to
+        write an 8 byte number in a 4 byte field.  */
+      the_insn.exp.X_add_number = 0;
+
+      /* Only need hh22 if `or' insn can't handle constant.  */
+      if (upper32 < -(1 << 12) || upper32 >= (1 << 12))
+       need_hh22_p = 1;
+
+      /* Does bottom part (after sethi) have bits?  */
+      if ((need_hh22_p && (upper32 & 0x3ff) != 0)
+         /* No hh22, but does upper32 still have bits we can't set
+            from lower32?  */
+         || (! need_hh22_p && upper32 != 0 && upper32 != -1))
+       need_hm10_p = 1;
+
+      /* If the lower half is all zero, we build the upper half directly
+        into the dst reg.  */
+      if (lower32 != 0
+         /* Need lower half if number is zero or 0xffffffff00000000.  */
+         || (! need_hh22_p && ! need_hm10_p))
+       {
+         /* No need for sethi if `or' insn can handle constant.  */
+         if (lower32 < -(1 << 12) || lower32 >= (1 << 12)
+             /* Note that we can't use a negative constant in the `or'
+                insn unless the upper 32 bits are all ones.  */
+             || (lower32 < 0 && upper32 != -1)
+             || (lower32 >= 0 && upper32 == -1))
+           need_hi22_p = 1;
+
+         if (need_hi22_p && upper32 == -1)
+           need_xor10_p = 1;
+
+         /* Does bottom part (after sethi) have bits?  */
+         else if ((need_hi22_p && (lower32 & 0x3ff) != 0)
+                  /* No sethi.  */
+                  || (! need_hi22_p && (lower32 & 0x1fff) != 0)
+                  /* Need `or' if we didn't set anything else.  */
+                  || (! need_hi22_p && ! need_hh22_p && ! need_hm10_p))
+           need_lo10_p = 1;
+       }
+      else
+       /* Output directly to dst reg if lower 32 bits are all zero.  */
+       upper_dstreg = dstreg;
+    }
+
+  if (!upper_dstreg && dstreg)
+    as_warn (_("setx: illegal temporary register g0"));
+
+  if (need_hh22_p)
+    {
+      the_insn.opcode = (SETHI_INSN | RD (upper_dstreg)
+                        | ((upper32 >> 10) & 0x3fffff));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+    }
+
+  if (need_hi22_p)
+    {
+      the_insn.opcode = (SETHI_INSN | RD (dstreg)
+                        | (((need_xor10_p ? ~lower32 : lower32)
+                            >> 10) & 0x3fffff));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_SPARC_LM22 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+    }
+
+  if (need_hm10_p)
+    {
+      the_insn.opcode = (OR_INSN
+                        | (need_hh22_p ? RS1 (upper_dstreg) : 0)
+                        | RD (upper_dstreg)
+                        | IMMED
+                        | (upper32 & (need_hh22_p ? 0x3ff : 0x1fff)));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_SPARC_HM10 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+    }
+
+  if (need_lo10_p)
+    {
+      /* FIXME: One nice optimization to do here is to OR the low part
+        with the highpart if hi22 isn't needed and the low part is
+        positive.  */
+      the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0)
+                        | RD (dstreg)
+                        | IMMED
+                        | (lower32 & (need_hi22_p ? 0x3ff : 0x1fff)));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_LO10 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+    }
+
+  /* If we needed to build the upper part, shift it into place.  */
+  if (need_hh22_p || need_hm10_p)
+    {
+      the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg)
+                        | IMMED | 32);
+      the_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &the_insn);
+    }
+
+  /* To get -1 in upper32, we do sethi %hi(~x), r; xor r, -0x400 | x, r.  */
+  if (need_xor10_p)
+    {
+      the_insn.opcode = (XOR_INSN | RS1 (dstreg) | RD (dstreg) | IMMED
+                        | 0x1c00 | (lower32 & 0x3ff));
+      the_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &the_insn);
+    }
+
+  /* If we needed to build both upper and lower parts, OR them together.  */
+  else if ((need_hh22_p || need_hm10_p) && (need_hi22_p || need_lo10_p))
+    {
+      the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
+                        | RD (dstreg));
+      the_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &the_insn);
+    }
+}
+\f
+/* Main entry point to assemble one instruction.  */
+
+void
+md_assemble (str)
+     char *str;
+{
+  const struct sparc_opcode *insn;
+  int special_case;
+
+  know (str);
+  special_case = sparc_ip (str, &insn);
+
+  /* We warn about attempts to put a floating point branch in a delay slot,
+     unless the delay slot has been annulled.  */
+  if (insn != NULL
+      && 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.  */
+  if (max_architecture < SPARC_OPCODE_ARCH_V9
+      && insn != NULL
+      && last_insn != NULL
+      && (insn->flags & F_FBR) != 0
+      && (last_insn->flags & F_FLOAT) != 0)
+    {
+      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"));
+    }
+
+  switch (special_case)
+    {
+    case SPECIAL_CASE_NONE:
+      /* Normal insn.  */
+      output_insn (insn, &the_insn);
+      break;
+
+    case SPECIAL_CASE_SETSW:
+      synthetize_setsw (insn);
+      break;
+
+    case SPECIAL_CASE_SET:
+      synthetize_setuw (insn);
+      break;
+
+    case SPECIAL_CASE_SETX:
+      synthetize_setx (insn);
+      break;
+
+    case SPECIAL_CASE_FDIV:
+      {
+       int rd = (the_insn.opcode >> 25) & 0x1f;
+
+       output_insn (insn, &the_insn);
+
+       /* According to information leaked from Sun, the "fdiv" instructions
+          on early SPARC machines would produce incorrect results sometimes.
+          The workaround is to add an fmovs of the destination register to
+          itself just after the instruction.  This was true on machines
+          with Weitek 1165 float chips, such as the Sun-4/260 and /280.  */
+       assert (the_insn.reloc == BFD_RELOC_NONE);
+       the_insn.opcode = FMOVS_INSN | rd | RD (rd);
+       output_insn (insn, &the_insn);
+       return;
+      }
+
+    default:
+      as_fatal (_("failed special case insn sanity check"));
+    }
+}
+
+/* Subroutine of md_assemble to do the actual parsing.  */
+
+static int
+sparc_ip (str, pinsn)
      char *str;
+     const struct sparc_opcode **pinsn;
 {
   char *error_message = "";
   char *s;
   const char *args;
   char c;
-  struct sparc_opcode *insn;
+  const struct sparc_opcode *insn;
   char *argsStart;
   unsigned long opcode;
   unsigned int mask = 0;
   int match = 0;
   int comma = 0;
-  long immediate_max = 0;
+  int v9_arg_p;
+  int special_case = SPECIAL_CASE_NONE;
 
-  for (s = str; islower (*s) || (*s >= '0' && *s <= '3'); ++s)
-    ;
-  switch (*s)
+  s = str;
+  if (islower ((unsigned char) *s))
     {
+      do
+       ++s;
+      while (islower ((unsigned char) *s) || isdigit ((unsigned char) *s));
+    }
 
+  switch (*s)
+    {
     case '\0':
       break;
 
     case ',':
       comma = 1;
-
-      /*FALLTHROUGH */
+      /* Fall through.  */
 
     case ' ':
       *s++ = '\0';
       break;
 
     default:
-      as_bad ("Unknown opcode: `%s'", str);
-      exit (1);
+      as_fatal (_("Unknown opcode: `%s'"), str);
     }
-  if ((insn = (struct sparc_opcode *) hash_find (op_hash, str)) == NULL)
+  insn = (struct sparc_opcode *) hash_find (op_hash, str);
+  *pinsn = insn;
+  if (insn == NULL)
     {
-      as_bad ("Unknown opcode: `%s'", str);
-      return;
+      as_bad (_("Unknown opcode: `%s'"), str);
+      return special_case;
     }
   if (comma)
     {
       *--s = ',';
     }
+
   argsStart = s;
   for (;;)
     {
       opcode = insn->match;
       memset (&the_insn, '\0', sizeof (the_insn));
       the_insn.reloc = BFD_RELOC_NONE;
+      v9_arg_p = 0;
 
-      /*
-       * Build the opcode, checking as we go to make
-       * sure that the operands match
-       */
+      /* Build the opcode, checking as we go to make sure that the
+         operands match.  */
       for (args = insn->args;; ++args)
        {
          switch (*args)
            {
-
-             /* start-sanitize-v9 */
-#ifndef NO_V9
            case 'K':
              {
                int kmask = 0;
-               int i;
 
                /* Parse a series of masks.  */
                if (*s == '#')
                  {
                    while (*s == '#')
                      {
-                       ++s;
-                       for (i = 0; i < MEMBAR_MASKS_SIZE; i++)
-                         if (!strncmp (s, membar_masks[i].name,
-                                       membar_masks[i].len))
-                           break;
-                       if (i < MEMBAR_MASKS_SIZE)
-                         {
-                           kmask |= membar_masks[i].mask;
-                           s += membar_masks[i].len;
-                         }
-                       else
+                       int mask;
+
+                       if (! parse_keyword_arg (sparc_encode_membar, &s,
+                                                &mask))
                          {
-                           error_message = ": invalid membar mask name";
+                           error_message = _(": invalid membar mask name");
                            goto error;
                          }
-                       if (*s == '|')
+                       kmask |= mask;
+                       while (*s == ' ')
+                         ++s;
+                       if (*s == '|' || *s == '+')
+                         ++s;
+                       while (*s == ' ')
                          ++s;
                      }
                  }
                else
                  {
-                   expressionS exp;
-                   char *hold;
-                   char *send;
-
-                   hold = input_line_pointer;
-                   input_line_pointer = s;
-                   expression (&exp);
-                   send = input_line_pointer;
-                   input_line_pointer = hold;
-
-                   kmask = exp.X_add_number;
-                   if (exp.X_op != O_constant
-                       || kmask < 0
-                       || kmask > 127)
+                   if (! parse_const_expr_arg (&s, &kmask))
                      {
-                       error_message = ": invalid membar mask number";
+                       error_message = _(": invalid membar mask expression");
+                       goto error;
+                     }
+                   if (kmask < 0 || kmask > 127)
+                     {
+                       error_message = _(": invalid membar mask number");
                        goto error;
                      }
-
-                   s = send;
                  }
 
-               opcode |= SIMM13 (kmask);
+               opcode |= MEMBAR (kmask);
+               continue;
+             }
+
+           case '3':
+             {
+               int smask = 0;
+
+               if (! parse_const_expr_arg (&s, &smask))
+                 {
+                   error_message = _(": invalid siam mode expression");
+                   goto error;
+                 }
+               if (smask < 0 || smask > 7)
+                 {
+                   error_message = _(": invalid siam mode number");
+                   goto error;
+                 }
+               opcode |= smask;
                continue;
              }
 
            case '*':
              {
-               int prefetch_fcn = 0;
+               int fcn = 0;
 
                /* Parse a prefetch function.  */
                if (*s == '#')
                  {
-                   s += 1;
-                   if (!strncmp (s, "n_reads", 7))
-                     prefetch_fcn = 0, s += 7;
-                   else if (!strncmp (s, "one_read", 8))
-                     prefetch_fcn = 1, s += 8;
-                   else if (!strncmp (s, "n_writes", 8))
-                     prefetch_fcn = 2, s += 8;
-                   else if (!strncmp (s, "one_write", 9))
-                     prefetch_fcn = 3, s += 9;
-                   else if (!strncmp (s, "page", 4))
-                     prefetch_fcn = 4, s += 4;
-                   else
+                   if (! parse_keyword_arg (sparc_encode_prefetch, &s, &fcn))
                      {
-                       error_message = ": invalid prefetch function name";
+                       error_message = _(": invalid prefetch function name");
                        goto error;
                      }
                  }
-               else if (isdigit (*s))
+               else
                  {
-                   while (isdigit (*s))
+                   if (! parse_const_expr_arg (&s, &fcn))
                      {
-                       prefetch_fcn = prefetch_fcn * 10 + *s - '0';
-                       ++s;
+                       error_message = _(": invalid prefetch function expression");
+                       goto error;
                      }
-
-                   if (prefetch_fcn < 0 || prefetch_fcn > 31)
+                   if (fcn < 0 || fcn > 31)
                      {
-                       error_message = ": invalid prefetch function number";
+                       error_message = _(": invalid prefetch function number");
                        goto error;
                      }
                  }
-               else
-                 {
-                   error_message = ": unrecognizable prefetch function";
-                   goto error;
-                 }
-               opcode |= RD (prefetch_fcn);
+               opcode |= RD (fcn);
                continue;
              }
 
            case '!':
            case '?':
-             /* Parse a privileged register.  */
+             /* Parse a sparc64 privileged register.  */
              if (*s == '%')
                {
                  struct priv_reg_entry *p = priv_reg_table;
-                 unsigned int len = 9999999; /* init to make gcc happy */
+                 unsigned int len = 9999999; /* Init to make gcc happy.  */
 
                  s += 1;
                  while (p->name[0] > s[0])
@@ -893,7 +1546,7 @@ sparc_ip (str)
                    }
                  if (p->name[0] != s[0])
                    {
-                     error_message = ": unrecognizable privileged register";
+                     error_message = _(": unrecognizable privileged register");
                      goto error;
                    }
                  if (*args == '?')
@@ -905,11 +1558,58 @@ sparc_ip (str)
                }
              else
                {
-                 error_message = ": unrecognizable privileged register";
+                 error_message = _(": unrecognizable privileged register");
+                 goto error;
+               }
+
+           case '_':
+           case '/':
+             /* Parse a v9a/v9b ancillary state register.  */
+             if (*s == '%')
+               {
+                 struct priv_reg_entry *p = v9a_asr_table;
+                 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])
+                   {
+                     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");
+                     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);
+                 s += len;
+                 continue;
+               }
+             else
+               {
+                 error_message = _(": unrecognizable v9a or v9b ancillary state register");
                  goto error;
                }
-#endif
-             /* end-sanitize-v9 */
 
            case 'M':
            case 'm':
@@ -917,44 +1617,70 @@ sparc_ip (str)
                {
                  s += 4;
 
-                 if (isdigit (*s))
+                 if (isdigit ((unsigned char) *s))
                    {
                      long num = 0;
 
-                     while (isdigit (*s))
+                     while (isdigit ((unsigned char) *s))
                        {
                          num = num * 10 + *s - '0';
                          ++s;
                        }
 
-                     if (num < 16 || 31 < num)
+                     if (current_architecture >= SPARC_OPCODE_ARCH_V9)
                        {
-                         error_message = ": asr number must be between 15 and 31";
-                         goto error;
-                       }       /* out of range */
+                         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;
+                           }
+                       }
 
                      opcode |= (*args == 'M' ? RS1 (num) : RD (num));
                      continue;
                    }
                  else
                    {
-                     error_message = ": expecting %asrN";
+                     error_message = _(": expecting %asrN");
                      goto error;
-                   }           /* if %asr followed by a number. */
-
-               }               /* if %asr */
+                   }
+               } /* if %asr  */
              break;
 
-             /* start-sanitize-v9 */
-#ifndef NO_V9
            case 'I':
              the_insn.reloc = BFD_RELOC_SPARC_11;
-             immediate_max = 0x03FF;
              goto immediate;
 
            case 'j':
              the_insn.reloc = BFD_RELOC_SPARC_10;
-             immediate_max = 0x01FF;
+             goto immediate;
+
+           case 'X':
+             /* V8 systems don't understand BFD_RELOC_SPARC_5.  */
+             if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+               the_insn.reloc = BFD_RELOC_SPARC_5;
+             else
+               the_insn.reloc = BFD_RELOC_SPARC13;
+             /* These fields are unsigned, but for upward compatibility,
+                allow negative values as well.  */
+             goto immediate;
+
+           case 'Y':
+             /* V8 systems don't understand BFD_RELOC_SPARC_6.  */
+             if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+               the_insn.reloc = BFD_RELOC_SPARC_6;
+             else
+               the_insn.reloc = BFD_RELOC_SPARC13;
+             /* These fields are unsigned, but for upward compatibility,
+                allow negative values as well.  */
              goto immediate;
 
            case 'k':
@@ -1070,10 +1796,8 @@ sparc_ip (str)
                  continue;
                }
              break;
-#endif /* NO_V9 */
-             /* end-sanitize-v9 */
 
-           case '\0':          /* end of args */
+           case '\0':          /* End of args.  */
              if (*s == '\0')
                {
                  match = 1;
@@ -1092,7 +1816,7 @@ sparc_ip (str)
                }
              break;
 
-           case '[':           /* these must match exactly */
+           case '[':           /* These must match exactly.  */
            case ']':
            case ',':
            case ' ':
@@ -1100,10 +1824,10 @@ sparc_ip (str)
                continue;
              break;
 
-           case '#':           /* must be at least one digit */
-             if (isdigit (*s++))
+           case '#':           /* Must be at least one digit.  */
+             if (isdigit ((unsigned char) *s++))
                {
-                 while (isdigit (*s))
+                 while (isdigit ((unsigned char) *s))
                    {
                      ++s;
                    }
@@ -1111,7 +1835,7 @@ sparc_ip (str)
                }
              break;
 
-           case 'C':           /* coprocessor state register */
+           case 'C':           /* Coprocessor state register.  */
              if (strncmp (s, "%csr", 4) == 0)
                {
                  s += 4;
@@ -1119,13 +1843,13 @@ sparc_ip (str)
                }
              break;
 
-           case 'b':           /* next operand is a coprocessor register */
+           case 'b':           /* Next operand is a coprocessor register.  */
            case 'c':
            case 'D':
-             if (*s++ == '%' && *s++ == 'c' && isdigit (*s))
+             if (*s++ == '%' && *s++ == 'c' && isdigit ((unsigned char) *s))
                {
                  mask = *s++;
-                 if (isdigit (*s))
+                 if (isdigit ((unsigned char) *s))
                    {
                      mask = 10 * (mask - '0') + (*s++ - '0');
                      if (mask >= 32)
@@ -1156,6 +1880,7 @@ sparc_ip (str)
              break;
 
            case 'r':           /* next operand must be a register */
+           case 'O':
            case '1':
            case '2':
            case 'd':
@@ -1173,7 +1898,8 @@ sparc_ip (str)
                      goto error;
 
                    case 'g':   /* global register */
-                     if (isoctal (c = *s++))
+                     c = *s++;
+                     if (isoctal (c))
                        {
                          mask = c - '0';
                          break;
@@ -1181,7 +1907,8 @@ sparc_ip (str)
                      goto error;
 
                    case 'i':   /* in register */
-                     if (isoctal (c = *s++))
+                     c = *s++;
+                     if (isoctal (c))
                        {
                          mask = c - '0' + 24;
                          break;
@@ -1189,7 +1916,8 @@ sparc_ip (str)
                      goto error;
 
                    case 'l':   /* local register */
-                     if (isoctal (c = *s++))
+                     c = *s++;
+                     if (isoctal (c))
                        {
                          mask = (c - '0' + 16);
                          break;
@@ -1197,7 +1925,8 @@ sparc_ip (str)
                      goto error;
 
                    case 'o':   /* out register */
-                     if (isoctal (c = *s++))
+                     c = *s++;
+                     if (isoctal (c))
                        {
                          mask = (c - '0' + 8);
                          break;
@@ -1213,7 +1942,7 @@ sparc_ip (str)
                      goto error;
 
                    case 'r':   /* any register */
-                     if (!isdigit (c = *s++))
+                     if (!isdigit ((unsigned char) (c = *s++)))
                        {
                          goto error;
                        }
@@ -1228,7 +1957,7 @@ sparc_ip (str)
                    case '7':
                    case '8':
                    case '9':
-                     if (isdigit (*s))
+                     if (isdigit ((unsigned char) *s))
                        {
                          if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
                            {
@@ -1245,13 +1974,15 @@ sparc_ip (str)
                    default:
                      goto error;
                    }
-                 /*
-                                        * Got the register, now figure out where
-                                        * it goes in the opcode.
-                                        */
+
+                 if ((mask & ~1) == 2 && sparc_arch_size == 64
+                     && no_undeclared_regs && ! globals[mask])
+                   as_bad (_("detected global register use not covered by .register pseudo-op"));
+
+                 /* Got the register, now figure out where
+                    it goes in the opcode.  */
                  switch (*args)
                    {
-
                    case '1':
                      opcode |= mask << 14;
                      continue;
@@ -1267,6 +1998,10 @@ sparc_ip (str)
                    case 'r':
                      opcode |= (mask << 25) | (mask << 14);
                      continue;
+
+                   case 'O':
+                     opcode |= (mask << 25) | (mask << 0);
+                     continue;
                    }
                }
              break;
@@ -1287,9 +2022,9 @@ sparc_ip (str)
 
                if (*s++ == '%'
                    && ((format = *s) == 'f')
-                   && isdigit (*++s))
+                   && isdigit ((unsigned char) *++s))
                  {
-                   for (mask = 0; isdigit (*s); ++s)
+                   for (mask = 0; isdigit ((unsigned char) *s); ++s)
                      {
                        mask = 10 * mask + (*s - '0');
                      }         /* read the number */
@@ -1310,43 +2045,41 @@ sparc_ip (str)
                        break;
                      }         /* register must be multiple of 4 */
 
-/* start-sanitize-v9 */
-#ifndef NO_V9
                    if (mask >= 64)
                      {
-                       error_message = ": There are only 64 f registers; [0-63]";
+                       if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+                         error_message = _(": There are only 64 f registers; [0-63]");
+                       else
+                         error_message = _(": There are only 32 f registers; [0-31]");
                        goto error;
                      } /* on error */
-                   if (mask >= 32)
-                     {
-                       mask -= 31;
-                     } /* wrap high bit */
-#else
-/* end-sanitize-v9 */
-                   if (mask >= 32)
+                   else if (mask >= 32)
                      {
-                       error_message = ": There are only 32 f registers; [0-31]";
-                       goto error;
-                     } /* on error */
-/* start-sanitize-v9 */
-#endif
-/* end-sanitize-v9 */
+                       if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+                         {
+                           v9_arg_p = 1;
+                           mask -= 31; /* wrap high bit */
+                         }
+                       else
+                         {
+                           error_message = _(": There are only 32 f registers; [0-31]");
+                           goto error;
+                         }
+                     }
                  }
                else
                  {
                    break;
-                 }     /* if not an 'f' register. */
+                 }     /* if not an 'f' register.  */
 
                switch (*args)
                  {
-
                  case 'v':
                  case 'V':
                  case 'e':
                    opcode |= RS1 (mask);
                    continue;
 
-
                  case 'f':
                  case 'B':
                  case 'R':
@@ -1358,11 +2091,11 @@ sparc_ip (str)
                  case 'J':
                    opcode |= RD (mask);
                    continue;
-                 }             /* pack it in. */
+                 }             /* Pack it in.  */
 
                know (0);
                break;
-             }                 /* float arg */
+             }                 /* float arg  */
 
            case 'F':
              if (strncmp (s, "%fsr", 4) == 0)
@@ -1372,172 +2105,280 @@ sparc_ip (str)
                }
              break;
 
-           case 'h':           /* high 22 bits */
-             the_insn.reloc = BFD_RELOC_HI22;
+           case '0':           /* 64 bit immediate (set, setsw, setx insn)  */
+             the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere  */
              goto immediate;
 
-           case 'l':           /* 22 bit PC relative immediate */
+           case 'l':           /* 22 bit PC relative immediate  */
              the_insn.reloc = BFD_RELOC_SPARC_WDISP22;
              the_insn.pcrel = 1;
              goto immediate;
 
-           case 'L':           /* 30 bit immediate */
+           case 'L':           /* 30 bit immediate  */
              the_insn.reloc = BFD_RELOC_32_PCREL_S2;
              the_insn.pcrel = 1;
              goto immediate;
 
-           case 'n':           /* 22 bit immediate */
+           case 'h':
+           case 'n':           /* 22 bit immediate  */
              the_insn.reloc = BFD_RELOC_SPARC22;
              goto immediate;
 
-           case 'i':           /* 13 bit immediate */
+           case 'i':           /* 13 bit immediate  */
              the_insn.reloc = BFD_RELOC_SPARC13;
-             immediate_max = 0x0FFF;
 
-             /*FALLTHROUGH */
+             /* fallthrough */
 
            immediate:
              if (*s == ' ')
                s++;
-             if (*s == '%')
-               {
-                 if ((c = s[1]) == 'h' && s[2] == 'i')
-                   {
-                     the_insn.reloc = BFD_RELOC_HI22;
-                     s += 3;
-                   }
-                 else if (c == 'l' && s[2] == 'o')
-                   {
-                     the_insn.reloc = BFD_RELOC_LO10;
-                     s += 3;
-                   }
-                 /* start-sanitize-v9 */
-#ifndef NO_V9
-                 else if (c == 'u'
-                          && s[2] == 'h'
-                          && s[3] == 'i')
-                   {
-                     the_insn.reloc = BFD_RELOC_SPARC_HH22;
-                     s += 4;
-                   }
-                 else if (c == 'u'
-                          && s[2] == 'l'
-                          && s[3] == 'o')
-                   {
-                     the_insn.reloc = BFD_RELOC_SPARC_HM10;
-                     s += 4;
-                   }
-#endif /* NO_V9 */
-                 /* end-sanitize-v9 */
-                 else
-                   break;
-               }
-             /* Note that if the getExpression() fails, we will still
-                have created U entries in the symbol table for the
-                'symbols' in the input string.  Try not to create U
-                symbols for registers, etc.  */
+
              {
+               char *s1;
+               char *op_arg = NULL;
+               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 },
+                     { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 },
+                     { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 },
+                     { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 },
+                     { "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 },
+                     { 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] != '(')
+                     {
+                       as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+                       return special_case;
+                     }
+
+                   op_arg = o->name;
+                   the_insn.reloc = o->reloc;
+                   s += o->len + 2;
+                   v9_arg_p = o->v9_p;
+                 }
+
+               /* Note that if the get_expression() fails, we will still
+                  have created U entries in the symbol table for the
+                  'symbols' in the input string.  Try not to create U
+                  symbols for registers, etc.  */
+
                /* This stuff checks to see if the expression ends in
                   +%reg.  If it does, it removes the register from
                   the expression, and re-sets 's' to point to the
                   right place.  */
 
-               char *s1;
+               if (op_arg)
+                 {
+                   int npar = 0;
+
+                   for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
+                     if (*s1 == '(')
+                       npar++;
+                     else if (*s1 == ')')
+                       {
+                         if (!npar)
+                           break;
+                         npar--;
+                       }
 
-               for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++);;
+                   if (*s1 != ')')
+                     {
+                       as_bad (_("Illegal operands: %%%s requires arguments in ()"), op_arg);
+                       return special_case;
+                     }
 
-               if (s1 != s && isdigit (s1[-1]))
-                 {
-                   if (s1[-2] == '%' && s1[-3] == '+')
+                   *s1 = '\0';
+                   (void) get_expression (s);
+                   *s1 = ')';
+                   s = s1 + 1;
+                   if (*s == ',' || *s == ']' || !*s)
+                     continue;
+                   if (*s != '+' && *s != '-')
                      {
-                       s1 -= 3;
-                       *s1 = '\0';
-                       (void) getExpression (s);
-                       *s1 = '+';
-                       s = s1;
-                       continue;
+                       as_bad (_("Illegal operands: Can't do arithmetics other than + and - involving %%%s()"), op_arg);
+                       return special_case;
                      }
+                   *s1 = '0';
+                   s = s1;
+                   op_exp = the_insn.exp;
+                   memset (&the_insn.exp, 0, sizeof (the_insn.exp));
+                 }
+
+               for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
+                 ;
+
+               if (s1 != s && isdigit ((unsigned char) s1[-1]))
+                 {
+                   if (s1[-2] == '%' && s1[-3] == '+')
+                     s1 -= 3;
                    else if (strchr ("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
+                     s1 -= 4;
+                   else
+                     s1 = NULL;
+                   if (s1)
                      {
-                       s1 -= 4;
                        *s1 = '\0';
-                       (void) getExpression (s);
+                       if (op_arg && s1 == s + 1)
+                         the_insn.exp.X_op = O_absent;
+                       else
+                         (void) get_expression (s);
                        *s1 = '+';
+                       if (op_arg)
+                         *s = ')';
                        s = s1;
-                       continue;
                      }
                  }
-             }
-             (void) getExpression (s);
-             s = expr_end;
+               else
+                 s1 = NULL;
 
-             if (the_insn.exp.X_op == O_constant
-                 && the_insn.exp.X_add_symbol == 0
-                 && the_insn.exp.X_op_symbol == 0)
-               {
-                 /* start-sanitize-v9 */
-#ifndef NO_V9
-                 /* Handle %uhi/%ulo by moving the upper word to the lower
-                    one and pretending it's %hi/%lo.  We also need to watch
-                    for %hi/%lo: the top word needs to be zeroed otherwise
-                    fixup_segment will complain the value is too big.  */
-                 switch (the_insn.reloc)
-                   {
-                   case BFD_RELOC_SPARC_HH22:
-                     the_insn.reloc = BFD_RELOC_HI22;
-                     the_insn.exp.X_add_number >>= 32;
-                     break;
-                   case BFD_RELOC_SPARC_HM10:
-                     the_insn.reloc = BFD_RELOC_LO10;
-                     the_insn.exp.X_add_number >>= 32;
-                     break;
-                   default:
-                     break;
-                   case BFD_RELOC_HI22:
-                   case BFD_RELOC_LO10:
-                     the_insn.exp.X_add_number &= 0xffffffff;
-                     break;
-                   }
-#endif
-                 /* end-sanitize-v9 */
-                 /* For pc-relative call instructions, we reject
-                    constants to get better code.  */
+               if (!s1)
+                 {
+                   (void) get_expression (s);
+                   if (op_arg)
+                     *s = ')';
+                   s = expr_end;
+                 }
+
+               if (op_arg)
+                 {
+                   the_insn.exp2 = the_insn.exp;
+                   the_insn.exp = op_exp;
+                   if (the_insn.exp2.X_op == O_absent)
+                     the_insn.exp2.X_op = O_illegal;
+                   else if (the_insn.exp.X_op == O_absent)
+                     {
+                       the_insn.exp = the_insn.exp2;
+                       the_insn.exp2.X_op = O_illegal;
+                     }
+                   else if (the_insn.exp.X_op == O_constant)
+                     {
+                       valueT val = the_insn.exp.X_add_number;
+                       switch (the_insn.reloc)
+                         {
+                         default:
+                           break;
+
+                         case BFD_RELOC_SPARC_HH22:
+                           val = BSR (val, 32);
+                           /* Fall through.  */
+
+                         case BFD_RELOC_SPARC_LM22:
+                         case BFD_RELOC_HI22:
+                           val = (val >> 10) & 0x3fffff;
+                           break;
+
+                         case BFD_RELOC_SPARC_HM10:
+                           val = BSR (val, 32);
+                           /* Fall through.  */
+
+                         case BFD_RELOC_LO10:
+                           val &= 0x3ff;
+                           break;
+
+                         case BFD_RELOC_SPARC_H44:
+                           val >>= 22;
+                           val &= 0x3fffff;
+                           break;
+
+                         case BFD_RELOC_SPARC_M44:
+                           val >>= 12;
+                           val &= 0x3ff;
+                           break;
+
+                         case BFD_RELOC_SPARC_L44:
+                           val &= 0xfff;
+                           break;
+
+                         case BFD_RELOC_SPARC_HIX22:
+                           val = ~val;
+                           val = (val >> 10) & 0x3fffff;
+                           break;
+
+                         case BFD_RELOC_SPARC_LOX10:
+                           val = (val & 0x3ff) | 0x1c00;
+                           break;
+                         }
+                       the_insn.exp = the_insn.exp2;
+                       the_insn.exp.X_add_number += val;
+                       the_insn.exp2.X_op = O_illegal;
+                       the_insn.reloc = old_reloc;
+                     }
+                   else if (the_insn.exp2.X_op != O_constant)
+                     {
+                       as_bad (_("Illegal operands: Can't add non-constant expression to %%%s()"), op_arg);
+                       return special_case;
+                     }
+                   else
+                     {
+                       if (old_reloc != BFD_RELOC_SPARC13
+                           || the_insn.reloc != BFD_RELOC_LO10
+                           || sparc_arch_size != 64
+                           || sparc_pic_code)
+                         {
+                           as_bad (_("Illegal operands: Can't do arithmetics involving %%%s() of a relocatable symbol"), op_arg);
+                           return special_case;
+                         }
+                       the_insn.reloc = BFD_RELOC_SPARC_OLO10;
+                     }
+                 }
+             }
+             /* Check for constants that don't require emitting a reloc.  */
+             if (the_insn.exp.X_op == O_constant
+                 && the_insn.exp.X_add_symbol == 0
+                 && the_insn.exp.X_op_symbol == 0)
+               {
+                 /* For pc-relative call instructions, we reject
+                    constants to get better code.  */
                  if (the_insn.pcrel
                      && the_insn.reloc == BFD_RELOC_32_PCREL_S2
-                     && in_signed_range (the_insn.exp.X_add_number, 0x3fff)
-                     )
+                     && in_signed_range (the_insn.exp.X_add_number, 0x3fff))
                    {
-                     error_message = ": PC-relative operand can't be a constant";
+                     error_message = _(": PC-relative operand can't be a constant");
                      goto error;
                    }
-                 /* Check for invalid constant values.  Don't warn if
-                    constant was inside %hi or %lo, since these
-                    truncate the constant to fit.  */
-                 if (immediate_max != 0
-                     && the_insn.reloc != BFD_RELOC_LO10
-                     && the_insn.reloc != BFD_RELOC_HI22
-                     && !in_signed_range (the_insn.exp.X_add_number,
-                                          immediate_max)
-                     )
-                   {
-                     if (the_insn.pcrel)
-                       /* Who knows?  After relocation, we may be within
-                          range.  Let the linker figure it out.  */
-                       {
-                         the_insn.exp.X_op = O_symbol;
-                         the_insn.exp.X_add_symbol = section_symbol (absolute_section);
-                       }
-                     else
-                       /* Immediate value is non-pcrel, and out of
-                           range.  */
-                       as_bad ("constant value %ld out of range (%ld .. %ld)",
-                               the_insn.exp.X_add_number,
-                               ~immediate_max, immediate_max);
-                   }
-               }
 
-             /* Reset to prevent extraneous range check.  */
-             immediate_max = 0;
+                 /* Constants that won't fit are checked in md_apply_fix3
+                    and bfd_install_relocation.
+                    ??? It would be preferable to install the constants
+                    into the insn here and save having to create a fixS
+                    for each one.  There already exists code to handle
+                    all the various cases (e.g. in md_apply_fix3 and
+                    bfd_install_relocation) so duplicating all that code
+                    here isn't right.  */
+               }
 
              continue;
 
@@ -1551,74 +2392,33 @@ sparc_ip (str)
 
            case 'A':
              {
-/* start-sanitize-v9 */
-#ifdef NO_V9
-/* end-sanitize-v9 */
-               char *push = input_line_pointer;
-               expressionS e;
-
-               input_line_pointer = s;
-
-               expression (&e);
-               if (e.X_op == O_constant)
-                 {
-                   opcode |= e.X_add_number << 5;
-                   s = input_line_pointer;
-                   input_line_pointer = push;
-                   continue;
-                 }             /* if absolute */
-
-               break;
-/* start-sanitize-v9 */
-#else
                int asi = 0;
 
                /* Parse an asi.  */
                if (*s == '#')
                  {
-                   s += 1;
-                   if (!strncmp (s, "ASI_AIUP", 8))
-                     asi = 0x10, s += 8;
-                   else if (!strncmp (s, "ASI_AIUS", 8))
-                     asi = 0x11, s += 8;
-                   else if (!strncmp (s, "ASI_PNF", 7))
-                     asi = 0x82, s += 7;
-                   else if (!strncmp (s, "ASI_SNF", 7))
-                     asi = 0x83, s += 7;
-                   else if (!strncmp (s, "ASI_P", 5))
-                     asi = 0x80, s += 5;
-                   else if (!strncmp (s, "ASI_S", 5))
-                     asi = 0x81, s += 5;
-                   else
+                   if (! parse_keyword_arg (sparc_encode_asi, &s, &asi))
                      {
-                       error_message = ": invalid asi name";
+                       error_message = _(": invalid ASI name");
                        goto error;
                      }
                  }
-               else if (isdigit (*s))
+               else
                  {
-                   char *push = input_line_pointer;
-                   input_line_pointer = s;
-                   asi = get_absolute_expression ();
-                   s = input_line_pointer;
-                   input_line_pointer = push;
-                   
+                   if (! parse_const_expr_arg (&s, &asi))
+                     {
+                       error_message = _(": invalid ASI expression");
+                       goto error;
+                     }
                    if (asi < 0 || asi > 255)
                      {
-                       error_message = ": invalid asi number";
+                       error_message = _(": invalid ASI number");
                        goto error;
                      }
                  }
-               else
-                 {
-                   error_message = ": unrecognizable asi";
-                   goto error;
-                 }
                opcode |= ASI (asi);
                continue;
-#endif
-/* end-sanitize-v9 */
-             }                 /* alternate space */
+             }                 /* Alternate space.  */
 
            case 'p':
              if (strncmp (s, "%psr", 4) == 0)
@@ -1628,7 +2428,7 @@ sparc_ip (str)
                }
              break;
 
-           case 'q':           /* floating point queue */
+           case 'q':           /* Floating point queue.  */
              if (strncmp (s, "%fq", 3) == 0)
                {
                  s += 3;
@@ -1636,7 +2436,7 @@ sparc_ip (str)
                }
              break;
 
-           case 'Q':           /* coprocessor queue */
+           case 'Q':           /* Coprocessor queue.  */
              if (strncmp (s, "%cq", 3) == 0)
                {
                  s += 3;
@@ -1645,11 +2445,22 @@ sparc_ip (str)
              break;
 
            case 'S':
-             if (strcmp (str, "set") == 0)
+             if (strcmp (str, "set") == 0
+                 || strcmp (str, "setuw") == 0)
                {
                  special_case = SPECIAL_CASE_SET;
                  continue;
                }
+             else if (strcmp (str, "setsw") == 0)
+               {
+                 special_case = SPECIAL_CASE_SETSW;
+                 continue;
+               }
+             else if (strcmp (str, "setx") == 0)
+               {
+                 special_case = SPECIAL_CASE_SETX;
+                 continue;
+               }
              else if (strncmp (str, "fdiv", 4) == 0)
                {
                  special_case = SPECIAL_CASE_FDIV;
@@ -1657,8 +2468,6 @@ sparc_ip (str)
                }
              break;
 
-             /* start-sanitize-v9 */
-#ifndef NO_V9
            case 'o':
              if (strncmp (s, "%asi", 4) != 0)
                break;
@@ -1676,8 +2485,6 @@ sparc_ip (str)
                break;
              s += 4;
              continue;
-#endif /* NO_V9 */
-             /* end-sanitize-v9 */
 
            case 't':
              if (strncmp (s, "%tbr", 4) != 0)
@@ -1691,23 +2498,63 @@ sparc_ip (str)
              s += 4;
              continue;
 
+           case 'x':
+             {
+               char *push = input_line_pointer;
+               expressionS e;
+
+               input_line_pointer = s;
+               expression (&e);
+               if (e.X_op == O_constant)
+                 {
+                   int n = e.X_add_number;
+                   if (n != e.X_add_number || (n & ~0x1ff) != 0)
+                     as_bad (_("OPF immediate operand out of range (0-0x1ff)"));
+                   else
+                     opcode |= e.X_add_number << 5;
+                 }
+               else
+                 as_bad (_("non-immediate OPF operand, ignored"));
+               s = input_line_pointer;
+               input_line_pointer = push;
+               continue;
+             }
+
            case 'y':
              if (strncmp (s, "%y", 2) != 0)
                break;
              s += 2;
              continue;
 
+           case 'u':
+           case 'U':
+             {
+               /* Parse a sparclet cpreg.  */
+               int cpreg;
+               if (! parse_keyword_arg (sparc_encode_sparclet_cpreg, &s, &cpreg))
+                 {
+                   error_message = _(": invalid cpreg name");
+                   goto error;
+                 }
+               opcode |= (*args == 'U' ? RS1 (cpreg) : RD (cpreg));
+               continue;
+             }
+
            default:
-             as_fatal ("failed sanity check.");
-           }                   /* switch on arg code */
+             as_fatal (_("failed sanity check."));
+           }                   /* switch on arg code.  */
+
+         /* Break out of for() loop.  */
          break;
-       }                       /* for each arg that we expect */
+       }                       /* For each arg that we expect.  */
+
     error:
       if (match == 0)
        {
-         /* Args don't match. */
-         if (((unsigned) (&insn[1] - sparc_opcodes)) < NUMOPCODES
-             && !strcmp (insn->name, insn[1].name))
+         /* Args don't match.  */
+         if (&insn[1] - sparc_opcodes < sparc_num_opcodes
+             && (insn->name == insn[1].name
+                 || !strcmp (insn->name, insn[1].name)))
            {
              ++insn;
              s = argsStart;
@@ -1715,57 +2562,158 @@ sparc_ip (str)
            }
          else
            {
-             as_bad ("Illegal operands%s", error_message);
-             return;
+             as_bad (_("Illegal operands%s"), error_message);
+             return special_case;
            }
        }
       else
        {
-         if (insn->architecture > current_architecture)
+         /* We have a match.  Now see if the architecture is OK.  */
+         int needed_arch_mask = insn->architecture;
+
+         if (v9_arg_p)
            {
-             if ((!architecture_requested || warn_on_bump)
-                 &&
-             /* start-sanitize-v9 */
-#ifndef NO_V9
-                 !ARCHITECTURES_CONFLICT_P (current_architecture,
-                                            insn->architecture)
-#else
-             /* end-sanitize-v9 */
-                 1
-             /* start-sanitize-v9 */
-#endif
-             /* end-sanitize-v9 */
-               )
-               {
-                 if (warn_on_bump)
-                   {
-                     as_warn ("architecture bumped from \"%s\" to \"%s\" on \"%s\"",
-                              architecture_pname[current_architecture],
-                              architecture_pname[insn->architecture],
-                              str);
-                   }           /* if warning */
+             needed_arch_mask &=
+               ~(SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9) - 1);
+             if (! needed_arch_mask)
+               needed_arch_mask =
+                 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9);
+           }
+
+         if (needed_arch_mask
+             & SPARC_OPCODE_SUPPORTED (current_architecture))
+           /* OK.  */
+           ;
+         /* Can we bump up the architecture?  */
+         else if (needed_arch_mask
+                  & SPARC_OPCODE_SUPPORTED (max_architecture))
+           {
+             enum sparc_opcode_arch_val needed_architecture =
+               sparc_ffs (SPARC_OPCODE_SUPPORTED (max_architecture)
+                          & needed_arch_mask);
 
-                 current_architecture = insn->architecture;
+             assert (needed_architecture <= SPARC_OPCODE_ARCH_MAX);
+             if (warn_on_bump
+                 && needed_architecture > warn_after_architecture)
+               {
+                 as_warn (_("architecture bumped from \"%s\" to \"%s\" on \"%s\""),
+                          sparc_opcode_archs[current_architecture].name,
+                          sparc_opcode_archs[needed_architecture].name,
+                          str);
+                 warn_after_architecture = needed_architecture;
                }
-             else
+             current_architecture = needed_architecture;
+           }
+         /* Conflict.  */
+         /* ??? This seems to be a bit fragile.  What if the next entry in
+            the opcode table is the one we want and it is supported?
+            It is possible to arrange the table today so that this can't
+            happen but what about tomorrow?  */
+         else
+           {
+             int arch, printed_one_p = 0;
+             char *p;
+             char required_archs[SPARC_OPCODE_ARCH_MAX * 16];
+
+             /* Create a list of the architectures that support the insn.  */
+             needed_arch_mask &= ~SPARC_OPCODE_SUPPORTED (max_architecture);
+             p = required_archs;
+             arch = sparc_ffs (needed_arch_mask);
+             while ((1 << arch) <= needed_arch_mask)
                {
-                 as_bad ("architecture mismatch on \"%s\" (\"%s\").  current architecture is \"%s\"",
-                         str,
-                         architecture_pname[insn->architecture],
-                         architecture_pname[current_architecture]);
-                 return;
-               }               /* if bump ok else error */
-           }                   /* if architecture higher */
-       }                       /* if no match */
+                 if ((1 << arch) & needed_arch_mask)
+                   {
+                     if (printed_one_p)
+                       *p++ = '|';
+                     strcpy (p, sparc_opcode_archs[arch].name);
+                     p += strlen (p);
+                     printed_one_p = 1;
+                   }
+                 ++arch;
+               }
+
+             as_bad (_("Architecture mismatch on \"%s\"."), str);
+             as_tsktsk (_(" (Requires %s; requested architecture is %s.)"),
+                        required_archs,
+                        sparc_opcode_archs[max_architecture].name);
+             return special_case;
+           }
+       } /* If no match.  */
 
       break;
-    }                          /* forever looking for a match */
+    } /* Forever looking for a match.  */
 
   the_insn.opcode = opcode;
+  return special_case;
+}
+
+/* Parse an argument that can be expressed as a keyword.
+   (eg: #StoreStore or %ccfr).
+   The result is a boolean indicating success.
+   If successful, INPUT_POINTER is updated.  */
+
+static int
+parse_keyword_arg (lookup_fn, input_pointerP, valueP)
+     int (*lookup_fn) PARAMS ((const char *));
+     char **input_pointerP;
+     int *valueP;
+{
+  int value;
+  char c, *p, *q;
+
+  p = *input_pointerP;
+  for (q = p + (*p == '#' || *p == '%');
+       isalnum ((unsigned char) *q) || *q == '_';
+       ++q)
+    continue;
+  c = *q;
+  *q = 0;
+  value = (*lookup_fn) (p);
+  *q = c;
+  if (value == -1)
+    return 0;
+  *valueP = value;
+  *input_pointerP = q;
+  return 1;
+}
+
+/* Parse an argument that is a constant expression.
+   The result is a boolean indicating success.  */
+
+static int
+parse_const_expr_arg (input_pointerP, valueP)
+     char **input_pointerP;
+     int *valueP;
+{
+  char *save = input_line_pointer;
+  expressionS exp;
+
+  input_line_pointer = *input_pointerP;
+  /* The next expression may be something other than a constant
+     (say if we're not processing the right variant of the insn).
+     Don't call expression unless we're sure it will succeed as it will
+     signal an error (which we want to defer until later).  */
+  /* FIXME: It might be better to define md_operand and have it recognize
+     things like %asi, etc. but continuing that route through to the end
+     is a lot of work.  */
+  if (*input_line_pointer == '%')
+    {
+      input_line_pointer = save;
+      return 0;
+    }
+  expression (&exp);
+  *input_pointerP = input_line_pointer;
+  input_line_pointer = save;
+  if (exp.X_op != O_constant)
+    return 0;
+  *valueP = exp.X_add_number;
+  return 1;
 }
 
+/* Subroutine of sparc_ip to parse an expression.  */
+
 static int
-getExpression (str)
+get_expression (str)
      char *str;
 {
   char *save_in;
@@ -1780,7 +2728,7 @@ getExpression (str)
       && seg != bss_section
       && seg != undefined_section)
     {
-      the_insn.error = "bad segment";
+      the_insn.error = _("bad segment");
       expr_end = input_line_pointer;
       input_line_pointer = save_in;
       return 1;
@@ -1788,19 +2736,58 @@ getExpression (str)
   expr_end = input_line_pointer;
   input_line_pointer = save_in;
   return 0;
-}                              /* getExpression() */
+}
+
+/* Subroutine of md_assemble to output one insn.  */
+
+static void
+output_insn (insn, the_insn)
+     const struct sparc_opcode *insn;
+     struct sparc_it *the_insn;
+{
+  char *toP = frag_more (4);
+
+  /* Put out the opcode.  */
+  if (INSN_BIG_ENDIAN)
+    number_to_chars_bigendian (toP, (valueT) the_insn->opcode, 4);
+  else
+    number_to_chars_littleendian (toP, (valueT) the_insn->opcode, 4);
+
+  /* Put out the symbol-dependent stuff.  */
+  if (the_insn->reloc != BFD_RELOC_NONE)
+    {
+      fixS *fixP =  fix_new_exp (frag_now,     /* Which frag.  */
+                                (toP - frag_now->fr_literal),  /* Where.  */
+                                4,             /* Size.  */
+                                &the_insn->exp,
+                                the_insn->pcrel,
+                                the_insn->reloc);
+      /* Turn off overflow checking in fixup_segment.  We'll do our
+        own overflow checking in md_apply_fix3.  This is necessary because
+        the insn size is 4 and fixup_segment will signal an overflow for
+        large 8 byte quantities.  */
+      fixP->fx_no_overflow = 1;
+      if (the_insn->reloc == BFD_RELOC_SPARC_OLO10)
+       fixP->tc_fix_data = the_insn->exp2.X_add_number;
+    }
 
+  last_insn = insn;
+  last_opcode = the_insn->opcode;
 
-/*
-  This is identical to the md_atof in m68k.c.  I think this is right,
-  but I'm not sure.
+#ifdef OBJ_ELF
+  dwarf2_emit_insn (4);
+#endif
+}
+\f
+/* This is identical to the md_atof in m68k.c.  I think this is right,
+   but I'm not sure.
 
-  Turn a string in input_line_pointer into a floating point constant of type
-  type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
-  emitted is stored in *sizeP .  An error message is returned, or NULL on OK.
-  */
+   Turn a string in input_line_pointer into a floating point constant
+   of type TYPE, and store the appropriate bytes in *LITP.  The number
+   of LITTLENUMS emitted is stored in *SIZEP.  An error message is
+   returned, or NULL on OK.  */
 
-/* Equal to MAX_PRECISION in atof-ieee.c */
+/* Equal to MAX_PRECISION in atof-ieee.c */
 #define MAX_LITTLENUMS 6
 
 char *
@@ -1809,15 +2796,12 @@ md_atof (type, litP, sizeP)
      char *litP;
      int *sizeP;
 {
-  int prec;
+  int i, prec;
   LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  LITTLENUM_TYPE *wordP;
   char *t;
-  char *atof_ieee ();
 
   switch (type)
     {
-
     case 'f':
     case 'F':
     case 's':
@@ -1844,55 +2828,100 @@ md_atof (type, litP, sizeP)
 
     default:
       *sizeP = 0;
-      return "Bad call to MD_ATOF()";
+      return _("Bad call to MD_ATOF()");
     }
+
   t = atof_ieee (input_line_pointer, type, words);
   if (t)
     input_line_pointer = t;
   *sizeP = prec * sizeof (LITTLENUM_TYPE);
-  for (wordP = words; prec--;)
+
+  if (target_big_endian)
+    {
+      for (i = 0; i < prec; i++)
+       {
+         md_number_to_chars (litP, (valueT) words[i],
+                             sizeof (LITTLENUM_TYPE));
+         litP += sizeof (LITTLENUM_TYPE);
+       }
+    }
+  else
     {
-      md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
-      litP += sizeof (LITTLENUM_TYPE);
+      for (i = prec - 1; i >= 0; i--)
+       {
+         md_number_to_chars (litP, (valueT) words[i],
+                             sizeof (LITTLENUM_TYPE));
+         litP += sizeof (LITTLENUM_TYPE);
+       }
     }
+
   return 0;
 }
 
-/*
- * Write out big-endian.
- */
+/* Write a value out to the object file, using the appropriate
+   endianness.  */
+
 void
 md_number_to_chars (buf, val, n)
      char *buf;
      valueT val;
      int n;
 {
-  number_to_chars_bigendian (buf, val, n);
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else if (target_little_endian_data
+          && ((n == 4 || n == 2) && ~now_seg->flags & SEC_ALLOC))
+    /* Output debug words, which are not in allocated sections, as big
+       endian.  */
+    number_to_chars_bigendian (buf, val, n);
+  else if (target_little_endian_data || ! target_big_endian)
+    number_to_chars_littleendian (buf, val, n);
 }
-
+\f
 /* Apply a fixS to the frags, now that we know the value it ought to
-   hold. */
+   hold.  */
 
 int
-md_apply_fix (fixP, value)
+md_apply_fix3 (fixP, value, segment)
      fixS *fixP;
      valueT *value;
+     segT segment;
 {
   char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
   offsetT val;
+  long insn;
 
   val = *value;
 
   assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
 
-  fixP->fx_addnumber = val;    /* Remember value for emit_reloc */
+  fixP->fx_addnumber = val;    /* Remember value for emit_reloc */
 
 #ifdef OBJ_ELF
   /* FIXME: SPARC ELF relocations don't use an addend in the data
      field itself.  This whole approach should be somehow combined
-     with the calls to bfd_perform_relocation.  */
+     with the calls to bfd_install_relocation.  Also, the value passed
+     in by fixup_segment includes the value of a defined symbol.  We
+     don't want to include the value of an externally visible symbol.  */
   if (fixP->fx_addsy != NULL)
-    return 1;
+    {
+      if (symbol_used_in_reloc_p (fixP->fx_addsy)
+         && (S_IS_EXTERNAL (fixP->fx_addsy)
+             || S_IS_WEAK (fixP->fx_addsy)
+             || (sparc_pic_code && ! fixP->fx_pcrel)
+             || (S_GET_SEGMENT (fixP->fx_addsy) != segment
+                 && ((bfd_get_section_flags (stdoutput,
+                                             S_GET_SEGMENT (fixP->fx_addsy))
+                      & SEC_LINK_ONCE) != 0
+                     || strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)),
+                                 ".gnu.linkonce",
+                                 sizeof ".gnu.linkonce" - 1) == 0)))
+         && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section
+         && S_GET_SEGMENT (fixP->fx_addsy) != undefined_section
+         && ! bfd_is_com_section (S_GET_SEGMENT (fixP->fx_addsy)))
+       fixP->fx_addnumber -= S_GET_VALUE (fixP->fx_addsy);
+      return 1;
+    }
 #endif
 
   /* This is a hack.  There should be a better way to
@@ -1901,163 +2930,332 @@ md_apply_fix (fixP, value)
   if (fixP->fx_r_type == BFD_RELOC_32_PCREL_S2 && fixP->fx_addsy)
     val += fixP->fx_where + fixP->fx_frag->fr_address;
 
-  switch (fixP->fx_r_type)
+#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))
     {
-    case BFD_RELOC_16:
-      buf[0] = val >> 8;
-      buf[1] = val;
-      break;
+      val = 0;
+      fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
+    }
+#endif
 
-    case BFD_RELOC_32:
-      buf[0] = val >> 24;
-      buf[1] = val >> 16;
-      buf[2] = val >> 8;
-      buf[3] = val;
-      break;
+  /* If this is a data relocation, just output VAL.  */
 
-    case BFD_RELOC_32_PCREL_S2:
-      val = (val >>= 2) + 1;
-      buf[0] |= (val >> 24) & 0x3f;
-      buf[1] = (val >> 16);
-      buf[2] = val >> 8;
-      buf[3] = val;
-      break;
+  if (fixP->fx_r_type == BFD_RELOC_16)
+    {
+      md_number_to_chars (buf, val, 2);
+    }
+  else if (fixP->fx_r_type == BFD_RELOC_32
+          || fixP->fx_r_type == BFD_RELOC_SPARC_REV32)
+    {
+      md_number_to_chars (buf, val, 4);
+    }
+  else if (fixP->fx_r_type == BFD_RELOC_64)
+    {
+      md_number_to_chars (buf, val, 8);
+    }
+  else if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+           || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    {
+      fixP->fx_done = 0;
+      return 1;
+    }
+  else
+    {
+      /* It's a relocation against an instruction.  */
 
-      /* start-sanitize-v9 */
-#ifndef NO_V9
-    case BFD_RELOC_64:
-      buf[0] = val >> 56;
-      buf[1] = val >> 48;
-      buf[2] = val >> 40;
-      buf[3] = val >> 32;
-      buf[4] = val >> 24;
-      buf[5] = val >> 16;
-      buf[6] = val >> 8;
-      buf[7] = val;
-      break;
+      if (INSN_BIG_ENDIAN)
+       insn = bfd_getb32 ((unsigned char *) buf);
+      else
+       insn = bfd_getl32 ((unsigned char *) buf);
 
-    case BFD_RELOC_SPARC_11:
-      if (((val > 0) && (val & ~0x7ff))
-         || ((val < 0) && (~(val - 1) & ~0x7ff)))
+      switch (fixP->fx_r_type)
        {
-         as_bad ("relocation overflow.");
-       }                       /* on overflow */
-
-      buf[2] |= (val >> 8) & 0x7;
-      buf[3] = val & 0xff;
-      break;
+       case BFD_RELOC_32_PCREL_S2:
+         val = val >> 2;
+         /* FIXME: This increment-by-one deserves a comment of why it's
+            being done!  */
+         if (! sparc_pic_code
+             || fixP->fx_addsy == NULL
+             || symbol_section_p (fixP->fx_addsy))
+           ++val;
+
+         insn |= val & 0x3fffffff;
+
+         /* See if we have a delay slot.  */
+         if (sparc_relax && fixP->fx_where + 8 <= fixP->fx_frag->fr_fix)
+           {
+#define G0             0
+#define O7             15
+#define XCC            (2 << 20)
+#define COND(x)                (((x)&0xf)<<25)
+#define CONDA          COND(0x8)
+#define INSN_BPA       (F2(0,1) | CONDA | BPRED | XCC)
+#define INSN_BA                (F2(0,2) | CONDA)
+#define INSN_OR                F3(2, 0x2, 0)
+#define INSN_NOP       F2(0,4)
+
+             long delay;
+
+             /* If the instruction is a call with either:
+                restore
+                arithmetic instruction with rd == %o7
+                where rs1 != %o7 and rs2 if it is register != %o7
+                then we can optimize if the call destination is near
+                by changing the call into a branch always.  */
+             if (INSN_BIG_ENDIAN)
+               delay = bfd_getb32 ((unsigned char *) buf + 4);
+             else
+               delay = bfd_getl32 ((unsigned char *) buf + 4);
+             if ((insn & OP (~0)) != OP (1) || (delay & OP (~0)) != OP (2))
+               break;
+             if ((delay & OP3 (~0)) != OP3 (0x3d) /* Restore.  */
+                 && ((delay & OP3 (0x28)) != 0 /* Arithmetic.  */
+                     || ((delay & RD (~0)) != RD (O7))))
+               break;
+             if ((delay & RS1 (~0)) == RS1 (O7)
+                 || ((delay & F3I (~0)) == 0
+                     && (delay & RS2 (~0)) == RS2 (O7)))
+               break;
+             /* Ensure the branch will fit into simm22.  */
+             if ((val & 0x3fe00000)
+                 && (val & 0x3fe00000) != 0x3fe00000)
+               break;
+             /* Check if the arch is v9 and branch will fit
+                into simm19.  */
+             if (((val & 0x3c0000) == 0
+                  || (val & 0x3c0000) == 0x3c0000)
+                 && (sparc_arch_size == 64
+                     || current_architecture >= SPARC_OPCODE_ARCH_V9))
+               /* ba,pt %xcc  */
+               insn = INSN_BPA | (val & 0x7ffff);
+             else
+               /* ba  */
+               insn = INSN_BA | (val & 0x3fffff);
+             if (fixP->fx_where >= 4
+                 && ((delay & (0xffffffff ^ RS1 (~0)))
+                     == (INSN_OR | RD (O7) | RS2 (G0))))
+               {
+                 long setter;
+                 int reg;
 
-    case BFD_RELOC_SPARC_10:
-      if (((val > 0) && (val & ~0x3ff))
-         || ((val < 0) && (~(val - 1) & ~0x3ff)))
-       {
-         as_bad ("relocation overflow.");
-       }                       /* on overflow */
+                 if (INSN_BIG_ENDIAN)
+                   setter = bfd_getb32 ((unsigned char *) buf - 4);
+                 else
+                   setter = bfd_getl32 ((unsigned char *) buf - 4);
+                 if ((setter & (0xffffffff ^ RD (~0)))
+                     != (INSN_OR | RS1 (O7) | RS2 (G0)))
+                   break;
+                 /* The sequence was
+                    or %o7, %g0, %rN
+                    call foo
+                    or %rN, %g0, %o7
+
+                    If call foo was replaced with ba, replace
+                    or %rN, %g0, %o7 with nop.  */
+                 reg = (delay & RS1 (~0)) >> 14;
+                 if (reg != ((setter & RD (~0)) >> 25)
+                     || reg == G0 || reg == O7)
+                   break;
 
-      buf[2] |= (val >> 8) & 0x3;
-      buf[3] = val & 0xff;
-      break;
+                 if (INSN_BIG_ENDIAN)
+                   bfd_putb32 (INSN_NOP, (unsigned char *) buf + 4);
+                 else
+                   bfd_putl32 (INSN_NOP, (unsigned char *) buf + 4);
+               }
+           }
+         break;
 
-    case BFD_RELOC_SPARC_WDISP16:
-      if (((val > 0) && (val & ~0x3fffc))
-         || ((val < 0) && (~(val - 1) & ~0x3fffc)))
-       {
-         as_bad ("relocation overflow.");
-       }                       /* on overflow */
+       case BFD_RELOC_SPARC_11:
+         if (! in_signed_range (val, 0x7ff))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x7ff;
+         break;
 
-      val = (val >>= 2) + 1;
-      buf[1] |= ((val >> 14) & 0x3) << 4;
-      buf[2] |= (val >> 8) & 0x3f;
-      buf[3] = val & 0xff;
-      break;
+       case BFD_RELOC_SPARC_10:
+         if (! in_signed_range (val, 0x3ff))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x3ff;
+         break;
 
-    case BFD_RELOC_SPARC_WDISP19:
-      if (((val > 0) && (val & ~0x1ffffc))
-         || ((val < 0) && (~(val - 1) & ~0x1ffffc)))
-       {
-         as_bad ("relocation overflow.");
-       }                       /* on overflow */
+       case BFD_RELOC_SPARC_7:
+         if (! in_bitfield_range (val, 0x7f))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x7f;
+         break;
 
-      val = (val >>= 2) + 1;
-      buf[1] |= (val >> 16) & 0x7;
-      buf[2] = (val >> 8) & 0xff;
-      buf[3] = val & 0xff;
-      break;
+       case BFD_RELOC_SPARC_6:
+         if (! in_bitfield_range (val, 0x3f))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x3f;
+         break;
 
-    case BFD_RELOC_SPARC_HH22:
-      val >>= 32;
-      /* intentional fallthrough */
-#endif /* NO_V9 */
-      /* end-sanitize-v9 */
+       case BFD_RELOC_SPARC_5:
+         if (! in_bitfield_range (val, 0x1f))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x1f;
+         break;
 
-      /* start-sanitize-v9 */
-#ifndef NO_V9
-    case BFD_RELOC_SPARC_LM22:
-#endif
-      /* end-sanitize-v9 */
-    case BFD_RELOC_HI22:
-      if (!fixP->fx_addsy)
-       {
-         buf[1] |= (val >> 26) & 0x3f;
-         buf[2] = val >> 18;
-         buf[3] = val >> 10;
-       }
-      else
-       {
-         buf[2] = 0;
-         buf[3] = 0;
-       }
-      break;
+       case BFD_RELOC_SPARC_WDISP16:
+         /* FIXME: simplify.  */
+         if (((val > 0) && (val & ~0x3fffc))
+             || ((val < 0) && (~(val - 1) & ~0x3fffc)))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         /* FIXME: The +1 deserves a comment.  */
+         val = (val >> 2) + 1;
+         insn |= ((val & 0xc000) << 6) | (val & 0x3fff);
+         break;
 
-    case BFD_RELOC_SPARC22:
-      if (val & ~0x003fffff)
-       {
-         as_bad ("relocation overflow");
-       }                       /* on overflow */
-      buf[1] |= (val >> 16) & 0x3f;
-      buf[2] = val >> 8;
-      buf[3] = val & 0xff;
-      break;
+       case BFD_RELOC_SPARC_WDISP19:
+         /* FIXME: simplify.  */
+         if (((val > 0) && (val & ~0x1ffffc))
+             || ((val < 0) && (~(val - 1) & ~0x1ffffc)))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         /* FIXME: The +1 deserves a comment.  */
+         val = (val >> 2) + 1;
+         insn |= val & 0x7ffff;
+         break;
 
-      /* start-sanitize-v9 */
-#ifndef NO_V9
-    case BFD_RELOC_SPARC_HM10:
-      val >>= 32;
-      /* intentional fallthrough */
-#endif /* NO_V9 */
-      /* end-sanitize-v9 */
+       case BFD_RELOC_SPARC_HH22:
+         val = BSR (val, 32);
+         /* Fall through.  */
 
-    case BFD_RELOC_LO10:
-      if (!fixP->fx_addsy)
-       {
-         buf[2] |= (val >> 8) & 0x03;
-         buf[3] = val;
-       }
-      else
-       buf[3] = 0;
-      break;
+       case BFD_RELOC_SPARC_LM22:
+       case BFD_RELOC_HI22:
+         if (!fixP->fx_addsy)
+           {
+             insn |= (val >> 10) & 0x3fffff;
+           }
+         else
+           {
+             /* FIXME: Need comment explaining why we do this.  */
+             insn &= ~0xffff;
+           }
+         break;
 
-    case BFD_RELOC_SPARC13:
-      if (! in_signed_range (val, 0x1fff))
-       as_bad ("relocation overflow");
+       case BFD_RELOC_SPARC22:
+         if (val & ~0x003fffff)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= (val & 0x3fffff);
+         break;
 
-      buf[2] |= (val >> 8) & 0x1f;
-      buf[3] = val;
-      break;
+       case BFD_RELOC_SPARC_HM10:
+         val = BSR (val, 32);
+         /* Fall through.  */
 
-    case BFD_RELOC_SPARC_WDISP22:
-      val = (val >>= 2) + 1;
-      /* FALLTHROUGH */
-    case BFD_RELOC_SPARC_BASE22:
-      buf[1] |= (val >> 16) & 0x3f;
-      buf[2] = val >> 8;
-      buf[3] = val;
-      break;
+       case BFD_RELOC_LO10:
+         if (!fixP->fx_addsy)
+           {
+             insn |= val & 0x3ff;
+           }
+         else
+           {
+             /* FIXME: Need comment explaining why we do this.  */
+             insn &= ~0xff;
+           }
+         break;
 
-    case BFD_RELOC_NONE:
-    default:
-      as_bad ("bad or unhandled relocation type: 0x%02x", fixP->fx_r_type);
-      break;
+       case BFD_RELOC_SPARC_OLO10:
+         val &= 0x3ff;
+         val += fixP->tc_fix_data;
+         /* Fall through.  */
+
+       case BFD_RELOC_SPARC13:
+         if (! in_signed_range (val, 0x1fff))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x1fff;
+         break;
+
+       case BFD_RELOC_SPARC_WDISP22:
+         val = (val >> 2) + 1;
+         /* Fall through.  */
+       case BFD_RELOC_SPARC_BASE22:
+         insn |= val & 0x3fffff;
+         break;
+
+       case BFD_RELOC_SPARC_H44:
+         if (!fixP->fx_addsy)
+           {
+             bfd_vma tval = val;
+             tval >>= 22;
+             insn |= tval & 0x3fffff;
+           }
+         break;
+
+       case BFD_RELOC_SPARC_M44:
+         if (!fixP->fx_addsy)
+           insn |= (val >> 12) & 0x3ff;
+         break;
+
+       case BFD_RELOC_SPARC_L44:
+         if (!fixP->fx_addsy)
+           insn |= val & 0xfff;
+         break;
+
+       case BFD_RELOC_SPARC_HIX22:
+         if (!fixP->fx_addsy)
+           {
+             val ^= ~(offsetT) 0;
+             insn |= (val >> 10) & 0x3fffff;
+           }
+         break;
+
+       case BFD_RELOC_SPARC_LOX10:
+         if (!fixP->fx_addsy)
+           insn |= 0x1c00 | (val & 0x3ff);
+         break;
+
+       case BFD_RELOC_NONE:
+       default:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("bad or unhandled relocation type: 0x%02x"),
+                       fixP->fx_r_type);
+         break;
+       }
+
+      if (INSN_BIG_ENDIAN)
+       bfd_putb32 (insn, (unsigned char *) buf);
+      else
+       bfd_putl32 (insn, (unsigned char *) buf);
     }
 
   /* Are we finished with this relocation now?  */
@@ -2069,18 +3267,21 @@ md_apply_fix (fixP, value)
 
 /* Translate internal representation of relocation info to BFD target
    format.  */
-arelent *
+
+arelent **
 tc_gen_reloc (section, fixp)
      asection *section;
      fixS *fixp;
 {
+  static arelent *relocs[3];
   arelent *reloc;
   bfd_reloc_code_real_type code;
 
-  reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent));
-  assert (reloc != 0);
+  relocs[0] = reloc = (arelent *) xmalloc (sizeof (arelent));
+  relocs[1] = NULL;
 
-  reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (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)
@@ -2091,10 +3292,15 @@ tc_gen_reloc (section, fixp)
     case BFD_RELOC_LO10:
     case BFD_RELOC_32_PCREL_S2:
     case BFD_RELOC_SPARC13:
+    case BFD_RELOC_SPARC22:
     case BFD_RELOC_SPARC_BASE13:
+    case BFD_RELOC_SPARC_WDISP16:
+    case BFD_RELOC_SPARC_WDISP19:
     case BFD_RELOC_SPARC_WDISP22:
-      /* start-sanitize-v9 */
     case BFD_RELOC_64:
+    case BFD_RELOC_SPARC_5:
+    case BFD_RELOC_SPARC_6:
+    case BFD_RELOC_SPARC_7:
     case BFD_RELOC_SPARC_10:
     case BFD_RELOC_SPARC_11:
     case BFD_RELOC_SPARC_HH22:
@@ -2103,232 +3309,156 @@ tc_gen_reloc (section, fixp)
     case BFD_RELOC_SPARC_PC_HH22:
     case BFD_RELOC_SPARC_PC_HM10:
     case BFD_RELOC_SPARC_PC_LM22:
-      /* end-sanitize-v9 */
+    case BFD_RELOC_SPARC_H44:
+    case BFD_RELOC_SPARC_M44:
+    case BFD_RELOC_SPARC_L44:
+    case BFD_RELOC_SPARC_HIX22:
+    case BFD_RELOC_SPARC_LOX10:
+    case BFD_RELOC_SPARC_REV32:
+    case BFD_RELOC_SPARC_OLO10:
+    case BFD_RELOC_VTABLE_ENTRY:
+    case BFD_RELOC_VTABLE_INHERIT:
       code = fixp->fx_r_type;
       break;
     default:
       abort ();
+      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
+
+  /* This code must be parallel to the OBJ_ELF tc_fix_adjustable.  */
+
+  if (sparc_pic_code)
+    {
+      switch (code)
+       {
+       case BFD_RELOC_32_PCREL_S2:
+         if (! S_IS_DEFINED (fixp->fx_addsy)
+             || S_IS_COMMON (fixp->fx_addsy)
+             || S_IS_EXTERNAL (fixp->fx_addsy)
+             || S_IS_WEAK (fixp->fx_addsy))
+           code = BFD_RELOC_SPARC_WPLT30;
+         break;
+       case BFD_RELOC_HI22:
+         if (fixp->fx_addsy != NULL
+             && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
+           code = BFD_RELOC_SPARC_PC22;
+         else
+           code = BFD_RELOC_SPARC_GOT22;
+         break;
+       case BFD_RELOC_LO10:
+         if (fixp->fx_addsy != NULL
+             && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
+           code = BFD_RELOC_SPARC_PC10;
+         else
+           code = BFD_RELOC_SPARC_GOT10;
+         break;
+       case BFD_RELOC_SPARC13:
+         code = BFD_RELOC_SPARC_GOT13;
+         break;
+       default:
+         break;
+       }
     }
-  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+#endif /* defined (OBJ_ELF) || defined (OBJ_AOUT)  */
+
+  if (code == BFD_RELOC_SPARC_OLO10)
+    reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO10);
+  else
+    reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
   if (reloc->howto == 0)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
-                   "internal error: can't export reloc type %d",
-                   fixp->fx_r_type);
-      return 0;
+                   _("internal error: can't export reloc type %d (`%s')"),
+                   fixp->fx_r_type, bfd_get_reloc_code_name (code));
+      xfree (reloc);
+      relocs[0] = NULL;
+      return relocs;
     }
-  assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
 
   /* @@ Why fx_addnumber sometimes and fx_offset other times?  */
-  if (reloc->howto->pc_relative == 0)
+#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;
-#if defined (OBJ_ELF) || defined (OBJ_COFF)
-  else if ((fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
-    reloc->addend = (section->vma
-                    + fixp->fx_addnumber
-                    + md_pcrel_from (fixp));
-#endif
   else
     reloc->addend = fixp->fx_offset - reloc->address;
 
-  return reloc;
-}
-
-
-#if 0
-/* for debugging only */
-static void
-print_insn (insn)
-     struct sparc_it *insn;
-{
-  const char *const Reloc[] = {
-    "RELOC_8",
-    "RELOC_16",
-    "RELOC_32",
-    "RELOC_DISP8",
-    "RELOC_DISP16",
-    "RELOC_DISP32",
-    "RELOC_WDISP30",
-    "RELOC_WDISP22",
-    "RELOC_HI22",
-    "RELOC_22",
-    "RELOC_13",
-    "RELOC_LO10",
-    "RELOC_SFA_BASE",
-    "RELOC_SFA_OFF13",
-    "RELOC_BASE10",
-    "RELOC_BASE13",
-    "RELOC_BASE22",
-    "RELOC_PC10",
-    "RELOC_PC22",
-    "RELOC_JMP_TBL",
-    "RELOC_SEGOFF16",
-    "RELOC_GLOB_DAT",
-    "RELOC_JMP_SLOT",
-    "RELOC_RELATIVE",
-    "NO_RELOC"
-  };
+#else /* elf or coff  */
 
-  if (insn->error)
-    fprintf (stderr, "ERROR: %s\n");
-  fprintf (stderr, "opcode=0x%08x\n", insn->opcode);
-  fprintf (stderr, "reloc = %s\n", Reloc[insn->reloc]);
-  fprintf (stderr, "exp = {\n");
-  fprintf (stderr, "\t\tX_add_symbol = %s\n",
-          ((insn->exp.X_add_symbol != NULL)
-           ? ((S_GET_NAME (insn->exp.X_add_symbol) != NULL)
-              ? S_GET_NAME (insn->exp.X_add_symbol)
-              : "???")
-           : "0"));
-  fprintf (stderr, "\t\tX_sub_symbol = %s\n",
-          ((insn->exp.X_op_symbol != NULL)
-           ? (S_GET_NAME (insn->exp.X_op_symbol)
-              ? S_GET_NAME (insn->exp.X_op_symbol)
-              : "???")
-           : "0"));
-  fprintf (stderr, "\t\tX_add_number = %d\n",
-          insn->exp.X_add_number);
-  fprintf (stderr, "}\n");
-}
+  if (reloc->howto->pc_relative == 0
+      || code == BFD_RELOC_SPARC_PC10
+      || code == BFD_RELOC_SPARC_PC22)
+    reloc->addend = fixp->fx_addnumber;
+  else if (symbol_section_p (fixp->fx_addsy))
+    reloc->addend = (section->vma
+                    + fixp->fx_addnumber
+                    + md_pcrel_from (fixp));
+  else
+    reloc->addend = fixp->fx_offset;
 #endif
 
-/*
- * md_parse_option
- *     Invocation line includes a switch not recognized by the base assembler.
- *     See if it's a processor-specific option.  These are:
- *
- *     -bump
- *             Warn on architecture bumps.  See also -A.
- *
- *     -Av6, -Av7, -Av8, -Asparclite
- *             Select the architecture.  Instructions or features not
- *             supported by the selected architecture cause fatal errors.
- *
- *             The default is to start at v6, and bump the architecture up
- *             whenever an instruction is seen at a higher level.
- *
- *             If -bump is specified, a warning is printing when bumping to
- *             higher levels.
- *
- *             If an architecture is specified, all instructions must match
- *             that architecture.  Any higher level instructions are flagged
- *             as errors.
- *
- *             if both an architecture and -bump are specified, the
- *             architecture starts at the specified level, but bumps are
- *             warnings.
- *
- * start-sanitize-v9
- *     -Av9
- *             Another architecture switch.
- *
- * Note:
- *             Bumping between incompatible architectures is always an
- *             error.  For example, from sparclite to v9.
- * end-sanitize-v9
- */
-
-int 
-md_parse_option (argP, cntP, vecP)
-     char **argP;
-     int *cntP;
-     char ***vecP;
-{
-  char *p;
-  const char **arch;
-
-  if (!strcmp (*argP, "bump"))
-    {
-      warn_on_bump = 1;
-    }
-  else if (**argP == 'A')
-    {
-      p = (*argP) + 1;
-
-      for (arch = architecture_pname; *arch != NULL; ++arch)
-       {
-         if (strcmp (p, *arch) == 0)
-           {
-             break;
-           }                   /* found a match */
-       }                       /* walk the pname table */
-
-      if (*arch == NULL)
-       {
-         as_bad ("unknown architecture: %s", p);
-       }
-      else
-       {
-         current_architecture = (enum sparc_architecture) (arch - architecture_pname);
-         architecture_requested = 1;
-       }
-    }
-#ifdef OBJ_ELF
-  else if (**argP == 'V')
-    {
-      print_version_id ();
-    }
-  else if (**argP == 'Q')
-    {
-      /* Qy - do emit .comment
-        Qn - do not emit .comment */
-    }
-  else if (**argP == 's')
-    {
-      /* use .stab instead of .stab.excl */
-    }
-  else if (**argP == 'q')
-    {
-      /* quick -- native assembler does fewer checks */
-    }
-#endif
-  else if (strcmp (*argP, "sparc") == 0)
-    {
-      /* Ignore -sparc, used by SunOS make default .s.o rule.  */
-    }
-  else
+  /* We expand R_SPARC_OLO10 to R_SPARC_LO10 and R_SPARC_13
+     on the same location.  */
+  if (code == BFD_RELOC_SPARC_OLO10)
     {
-      /* Unknown option */
-      (*argP)++;
-      return 0;
+      relocs[1] = reloc = (arelent *) xmalloc (sizeof (arelent));
+      relocs[2] = NULL;
+
+      reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+      *reloc->sym_ptr_ptr
+       = symbol_get_bfdsym (section_symbol (absolute_section));
+      reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+      reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_SPARC13);
+      reloc->addend = fixp->tc_fix_data;
     }
-  **argP = '\0';               /* Done parsing this switch */
-  return 1;
-}                              /* md_parse_option() */
 
-/* We have no need to default values of symbols. */
+  return relocs;
+}
+\f
+/* We have no need to default values of symbols.  */
 
-/* ARGSUSED */
 symbolS *
 md_undefined_symbol (name)
-     char *name;
+     char *name ATTRIBUTE_UNUSED;
 {
   return 0;
-}                              /* md_undefined_symbol() */
-
-/* Parse an operand that is machine-specific.
-   We just return without modifying the expression if we have nothing
-   to do. */
-
-/* ARGSUSED */
-void 
-md_operand (expressionP)
-     expressionS *expressionP;
-{
 }
 
-/* Round up a section size to the appropriate boundary. */
+/* Round up a section size to the appropriate boundary.  */
+
 valueT
 md_section_align (segment, size)
-     segT segment;
+     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) (stdoutput->xvec->align_power_min);
+  valueT align = ((valueT) 1
+                 << (valueT) bfd_get_section_alignment (stdoutput, segment));
   valueT newsize;
-  /* turn alignment value into a mask */
+
+  /* Turn alignment value into a mask.  */
   align--;
   newsize = (size + align) & ~align;
   return newsize;
@@ -2340,12 +3470,747 @@ md_section_align (segment, size)
 /* Exactly what point is a PC-relative offset relative TO?
    On the sparc, they're relative to the address of the offset, plus
    its size.  This gets us to the following instruction.
-   (??? Is this right?  FIXME-SOON) */
-long 
+   (??? Is this right?  FIXME-SOON)  */
+long
 md_pcrel_from (fixP)
      fixS *fixP;
 {
-  return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+  long ret;
+
+  ret = fixP->fx_where + fixP->fx_frag->fr_address;
+  if (! sparc_pic_code
+      || fixP->fx_addsy == NULL
+      || symbol_section_p (fixP->fx_addsy))
+    ret += fixP->fx_size;
+  return ret;
+}
+\f
+/* Return log2 (VALUE), or -1 if VALUE is not an exact positive power
+   of two.  */
+
+static int
+log2 (value)
+     int value;
+{
+  int shift;
+
+  if (value <= 0)
+    return -1;
+
+  for (shift = 0; (value & 1) == 0; value >>= 1)
+    ++shift;
+
+  return (value == 1) ? shift : -1;
 }
 
-/* end of tc-sparc.c */
+/* Sort of like s_lcomm.  */
+
+#ifndef OBJ_ELF
+static int max_alignment = 15;
+#endif
+
+static void
+s_reserve (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  char *name;
+  char *p;
+  char c;
+  int align;
+  int size;
+  int temp;
+  symbolS *symbolP;
+
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  p = input_line_pointer;
+  *p = c;
+  SKIP_WHITESPACE ();
+
+  if (*input_line_pointer != ',')
+    {
+      as_bad (_("Expected comma after name"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  ++input_line_pointer;
+
+  if ((size = get_absolute_expression ()) < 0)
+    {
+      as_bad (_("BSS length (%d.) <0! Ignored."), size);
+      ignore_rest_of_line ();
+      return;
+    }                          /* Bad length.  */
+
+  *p = 0;
+  symbolP = symbol_find_or_make (name);
+  *p = c;
+
+  if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0
+      && strncmp (input_line_pointer, ",\".bss\"", 7) != 0)
+    {
+      as_bad (_("bad .reserve segment -- expected BSS segment"));
+      return;
+    }
+
+  if (input_line_pointer[2] == '.')
+    input_line_pointer += 7;
+  else
+    input_line_pointer += 6;
+  SKIP_WHITESPACE ();
+
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
+
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer == '\n')
+       {
+         as_bad (_("missing alignment"));
+         ignore_rest_of_line ();
+         return;
+       }
+
+      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"));
+         ignore_rest_of_line ();
+         return;
+       }
+
+      if (align != 0)
+       {
+         temp = log2 (align);
+         if (temp < 0)
+           {
+             as_bad (_("alignment not a power of 2"));
+             ignore_rest_of_line ();
+             return;
+           }
+
+         align = temp;
+       }
+
+      record_alignment (bss_section, align);
+    }
+  else
+    align = 0;
+
+  if (!S_IS_DEFINED (symbolP)
+#ifdef OBJ_AOUT
+      && S_GET_OTHER (symbolP) == 0
+      && S_GET_DESC (symbolP) == 0
+#endif
+      )
+    {
+      if (! need_pass_2)
+       {
+         char *pfrag;
+         segT current_seg = now_seg;
+         subsegT current_subseg = now_subseg;
+
+         /* Switch to bss.  */
+         subseg_set (bss_section, 1);
+
+         if (align)
+           /* Do alignment.  */
+           frag_align (align, 0, 0);
+
+         /* Detach from old frag.  */
+         if (S_GET_SEGMENT (symbolP) == bss_section)
+           symbol_get_frag (symbolP)->fr_symbol = NULL;
+
+         symbol_set_frag (symbolP, frag_now);
+         pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+                           (offsetT) size, (char *) 0);
+         *pfrag = 0;
+
+         S_SET_SEGMENT (symbolP, bss_section);
+
+         subseg_set (current_seg, current_subseg);
+
+#ifdef OBJ_ELF
+         S_SET_SIZE (symbolP, size);
+#endif
+       }
+    }
+  else
+    {
+      as_warn ("Ignoring attempt to re-define symbol %s",
+              S_GET_NAME (symbolP));
+    }                          /* if not redefining.  */
+
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_common (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  char *name;
+  char c;
+  char *p;
+  int temp, size;
+  symbolS *symbolP;
+
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  /* Just after name is now '\0'.  */
+  p = input_line_pointer;
+  *p = c;
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer != ',')
+    {
+      as_bad (_("Expected comma after symbol-name"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  /* Skip ','.  */
+  input_line_pointer++;
+
+  if ((temp = get_absolute_expression ()) < 0)
+    {
+      as_bad (_(".COMMon length (%d.) <0! Ignored."), temp);
+      ignore_rest_of_line ();
+      return;
+    }
+  size = temp;
+  *p = 0;
+  symbolP = symbol_find_or_make (name);
+  *p = c;
+  if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+    {
+      as_bad (_("Ignoring attempt to re-define symbol"));
+      ignore_rest_of_line ();
+      return;
+    }
+  if (S_GET_VALUE (symbolP) != 0)
+    {
+      if (S_GET_VALUE (symbolP) != (valueT) size)
+       {
+         as_warn (_("Length of .comm \"%s\" is already %ld. Not changed to %d."),
+                  S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), 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 != ',')
+    {
+      as_bad (_("Expected comma after common length"));
+      ignore_rest_of_line ();
+      return;
+    }
+  input_line_pointer++;
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer != '"')
+    {
+      temp = get_absolute_expression ();
+
+#ifndef OBJ_ELF
+      if (temp > max_alignment)
+       {
+         temp = max_alignment;
+         as_warn (_("alignment too large; assuming %d"), temp);
+       }
+#endif
+
+      if (temp < 0)
+       {
+         as_bad (_("negative alignment"));
+         ignore_rest_of_line ();
+         return;
+       }
+
+#ifdef OBJ_ELF
+      if (symbol_get_obj (symbolP)->local)
+       {
+         segT old_sec;
+         int old_subsec;
+         char *p;
+         int align;
+
+         old_sec = now_seg;
+         old_subsec = now_subseg;
+
+         if (temp == 0)
+           align = 0;
+         else
+           align = log2 (temp);
+
+         if (align < 0)
+           {
+             as_bad (_("alignment not a power of 2"));
+             ignore_rest_of_line ();
+             return;
+           }
+
+         record_alignment (bss_section, align);
+         subseg_set (bss_section, 0);
+         if (align)
+           frag_align (align, 0, 0);
+         if (S_GET_SEGMENT (symbolP) == bss_section)
+           symbol_get_frag (symbolP)->fr_symbol = 0;
+         symbol_set_frag (symbolP, frag_now);
+         p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+                       (offsetT) size, (char *) 0);
+         *p = 0;
+         S_SET_SEGMENT (symbolP, bss_section);
+         S_CLEAR_EXTERNAL (symbolP);
+         S_SET_SIZE (symbolP, size);
+         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);
+       }
+    }
+  else
+    {
+      input_line_pointer++;
+      /* @@ Some use the dot, some don't.  Can we get some consistency??  */
+      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))
+       {
+         while (*--input_line_pointer != '"')
+           ;
+         input_line_pointer--;
+         goto bad_common_segment;
+       }
+      while (*input_line_pointer++ != '"')
+       ;
+      goto allocate_common;
+    }
+
+#ifdef BFD_ASSEMBLER
+  symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+#endif
+
+  demand_empty_rest_of_line ();
+  return;
+
+  {
+  bad_common_segment:
+    p = input_line_pointer;
+    while (*p && *p != '\n')
+      p++;
+    c = *p;
+    *p = '\0';
+    as_bad (_("bad .common segment %s"), input_line_pointer + 1);
+    *p = c;
+    input_line_pointer = p;
+    ignore_rest_of_line ();
+    return;
+  }
+}
+
+/* Handle the .empty pseudo-op.  This supresses the warnings about
+   invalid delay slot usage.  */
+
+static void
+s_empty (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  /* The easy way to implement is to just forget about the last
+     instruction.  */
+  last_insn = NULL;
+}
+
+static void
+s_seg (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+
+  if (strncmp (input_line_pointer, "\"text\"", 6) == 0)
+    {
+      input_line_pointer += 6;
+      s_text (0);
+      return;
+    }
+  if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
+    {
+      input_line_pointer += 6;
+      s_data (0);
+      return;
+    }
+  if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
+    {
+      input_line_pointer += 7;
+      s_data1 ();
+      return;
+    }
+  if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
+    {
+      input_line_pointer += 5;
+      /* We only support 2 segments -- text and data -- for now, so
+        things in the "bss segment" will have to go into data for now.
+        You can still allocate SEG_BSS stuff with .lcomm or .reserve.  */
+      subseg_set (data_section, 255);  /* FIXME-SOMEDAY.  */
+      return;
+    }
+  as_bad (_("Unknown segment type"));
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_data1 ()
+{
+  subseg_set (data_section, 1);
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_proc (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+    {
+      ++input_line_pointer;
+    }
+  ++input_line_pointer;
+}
+
+/* This static variable is set by s_uacons to tell sparc_cons_align
+   that the expession does not need to be aligned.  */
+
+static int sparc_no_align_cons = 0;
+
+/* This handles the unaligned space allocation pseudo-ops, such as
+   .uaword.  .uaword is just like .word, but the value does not need
+   to be aligned.  */
+
+static void
+s_uacons (bytes)
+     int bytes;
+{
+  /* Tell sparc_cons_align not to align this value.  */
+  sparc_no_align_cons = 1;
+  cons (bytes);
+}
+
+/* This handles the native word allocation pseudo-op .nword.
+   For sparc_arch_size 32 it is equivalent to .word,  for
+   sparc_arch_size 64 it is equivalent to .xword.  */
+
+static void
+s_ncons (bytes)
+     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:
+
+   .register %g[2367],{#scratch|symbolname|#ignore}
+*/
+
+static void
+s_register (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  char c;
+  int reg;
+  int flags;
+  const char *regname;
+
+  if (input_line_pointer[0] != '%'
+      || input_line_pointer[1] != 'g'
+      || ((input_line_pointer[2] & ~1) != '2'
+         && (input_line_pointer[2] & ~1) != '6')
+      || input_line_pointer[3] != ',')
+    as_bad (_("register syntax is .register %%g[2367],{#scratch|symbolname|#ignore}"));
+  reg = input_line_pointer[2] - '0';
+  input_line_pointer += 4;
+
+  if (*input_line_pointer == '#')
+    {
+      ++input_line_pointer;
+      regname = input_line_pointer;
+      c = get_symbol_end ();
+      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 = "";
+    }
+  else
+    {
+      regname = input_line_pointer;
+      c = get_symbol_end ();
+    }
+  if (sparc_arch_size == 64)
+    {
+      if (globals[reg])
+       {
+         if ((regname && globals[reg] != (symbolS *) 1
+              && strcmp (S_GET_NAME (globals[reg]), regname))
+             || ((regname != NULL) ^ (globals[reg] != (symbolS *) 1)))
+           as_bad (_("redefinition of global register"));
+       }
+      else
+       {
+         if (regname == NULL)
+           globals[reg] = (symbolS *) 1;
+         else
+           {
+             if (*regname)
+               {
+                 if (symbol_find (regname))
+                   as_bad (_("Register symbol %s already defined."),
+                           regname);
+               }
+             globals[reg] = symbol_make (regname);
+             flags = symbol_get_bfdsym (globals[reg])->flags;
+             if (! *regname)
+               flags = flags & ~(BSF_GLOBAL|BSF_LOCAL|BSF_WEAK);
+             if (! (flags & (BSF_GLOBAL|BSF_LOCAL|BSF_WEAK)))
+               flags |= BSF_GLOBAL;
+             symbol_get_bfdsym (globals[reg])->flags = flags;
+             S_SET_VALUE (globals[reg], (valueT) reg);
+             S_SET_ALIGN (globals[reg], reg);
+             S_SET_SIZE (globals[reg], 0);
+             /* Although we actually want undefined_section here,
+                we have to use absolute_section, because otherwise
+                generic as code will make it a COM section.
+                We fix this up in sparc_adjust_symtab.  */
+             S_SET_SEGMENT (globals[reg], absolute_section);
+             S_SET_OTHER (globals[reg], 0);
+             elf_symbol (symbol_get_bfdsym (globals[reg]))
+               ->internal_elf_sym.st_info =
+                 ELF_ST_INFO(STB_GLOBAL, STT_REGISTER);
+             elf_symbol (symbol_get_bfdsym (globals[reg]))
+               ->internal_elf_sym.st_shndx = SHN_UNDEF;
+           }
+       }
+    }
+
+  *input_line_pointer = c;
+
+  demand_empty_rest_of_line ();
+}
+
+/* Adjust the symbol table.  We set undefined sections for STT_REGISTER
+   symbols which need it.  */
+
+void
+sparc_adjust_symtab ()
+{
+  symbolS *sym;
+
+  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+    {
+      if (ELF_ST_TYPE (elf_symbol (symbol_get_bfdsym (sym))
+                      ->internal_elf_sym.st_info) != STT_REGISTER)
+       continue;
+
+      if (ELF_ST_TYPE (elf_symbol (symbol_get_bfdsym (sym))
+                      ->internal_elf_sym.st_shndx != SHN_UNDEF))
+       continue;
+
+      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
+   rs_align_code frag, and checking in HANDLE_ALIGN to make sure that
+   no unexpected alignment was introduced.
+
+   The SunOS and Solaris native assemblers enforce aligned data by
+   default.  We don't want to do that, because gcc can deliberately
+   generate misaligned data if the packed attribute is used.  Instead,
+   we permit misaligned data by default, and permit the user to set an
+   option to check for it.  */
+
+void
+sparc_cons_align (nbytes)
+     int nbytes;
+{
+  int nalign;
+  char *p;
+
+  /* Only do this if we are enforcing aligned data.  */
+  if (! enforce_aligned_data)
+    return;
+
+  if (sparc_no_align_cons)
+    {
+      /* This is an unaligned pseudo-op.  */
+      sparc_no_align_cons = 0;
+      return;
+    }
+
+  nalign = log2 (nbytes);
+  if (nalign == 0)
+    return;
+
+  assert (nalign > 0);
+
+  if (now_seg == absolute_section)
+    {
+      if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+       as_bad (_("misaligned data"));
+      return;
+    }
+
+  p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
+               (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+
+  record_alignment (now_seg, nalign);
+}
+
+/* This is called from HANDLE_ALIGN in tc-sparc.h.  */
+
+void
+sparc_handle_align (fragp)
+     fragS *fragp;
+{
+  int count, fix;
+  char *p;
+
+  count = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+
+  switch (fragp->fr_type)
+    {
+    case rs_align_test:
+      if (count != 0)
+       as_bad_where (fragp->fr_file, fragp->fr_line, _("misaligned data"));
+      break;
+
+    case rs_align_code:
+      p = fragp->fr_literal + fragp->fr_fix;
+      fix = 0;
+
+      if (count & 3)
+       {
+         fix = count & 3;
+         memset (p, 0, fix);
+         p += fix;
+         count -= fix;
+       }
+
+      if (SPARC_OPCODE_ARCH_V9_P (max_architecture) && count > 8)
+       {
+         unsigned wval = (0x30680000 | count >> 2); /* ba,a,pt %xcc, 1f  */
+         if (INSN_BIG_ENDIAN)
+           number_to_chars_bigendian (p, wval, 4);
+         else
+           number_to_chars_littleendian (p, wval, 4);
+         p += 4;
+         count -= 4;
+         fix += 4;
+       }
+
+      if (INSN_BIG_ENDIAN)
+       number_to_chars_bigendian (p, 0x01000000, 4);
+      else
+       number_to_chars_littleendian (p, 0x01000000, 4);
+
+      fragp->fr_fix += fix;
+      fragp->fr_var = 4;
+      break;
+
+    default:
+      break;
+    }
+}
+
+#ifdef OBJ_ELF
+/* Some special processing for a Sparc ELF file.  */
+
+void
+sparc_elf_final_processing ()
+{
+  /* Set the Sparc ELF flag bits.  FIXME: There should probably be some
+     sort of BFD interface for this.  */
+  if (sparc_arch_size == 64)
+    {
+      switch (sparc_memory_model)
+       {
+       case MM_RMO:
+         elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_RMO;
+         break;
+       case MM_PSO:
+         elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_PSO;
+         break;
+       default:
+         break;
+       }
+    }
+  else if (current_architecture >= SPARC_OPCODE_ARCH_V9)
+    elf_elfheader (stdoutput)->e_flags |= EF_SPARC_32PLUS;
+  if (current_architecture == SPARC_OPCODE_ARCH_V9A)
+    elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1;
+  else if (current_architecture == SPARC_OPCODE_ARCH_V9B)
+    elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1|EF_SPARC_SUN_US3;
+}
+#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.  */
+
+void
+cons_fix_new_sparc (frag, where, nbytes, exp)
+     fragS *frag;
+     int where;
+     unsigned int nbytes;
+     expressionS *exp;
+{
+  bfd_reloc_code_real_type r;
+
+  r = (nbytes == 1 ? BFD_RELOC_8 :
+       (nbytes == 2 ? BFD_RELOC_16 :
+       (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
+
+  if (target_little_endian_data && nbytes == 4
+      && now_seg->flags & SEC_ALLOC)
+    r = BFD_RELOC_SPARC_REV32;
+  fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
+}
+
+#ifdef OBJ_ELF
+int
+elf32_sparc_force_relocation (fixp)
+     struct fix *fixp;
+{
+  if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    return 1;
+
+  return 0;
+}
+#endif
This page took 0.086759 seconds and 4 git commands to generate.