* dwarf2dbg.c (struct line_entry): Replace frag and frag_ofs
[deliverable/binutils-gdb.git] / gas / config / tc-ia64.c
index 1d43e7773f4b2a46ad7b2d5dd7498a26398b159f..5875551c78d8f265a510e179f936a226cbdeda5d 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
-   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
    Free Software Foundation, Inc.
    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
 
@@ -17,8 +17,8 @@
 
    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.  */
+   the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
 
 /*
   TODO:
 
 #include "elf/ia64.h"
 
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
 #define NELEMS(a)      ((int) (sizeof (a)/sizeof ((a)[0])))
+
+/* Some systems define MIN in, e.g., param.h.  */
+#undef MIN
 #define MIN(a,b)       ((a) < (b) ? (a) : (b))
 
 #define NUM_SLOTS      4
@@ -152,6 +159,7 @@ struct label_fix
 {
   struct label_fix *next;
   struct symbol *sym;
+  bfd_boolean dw2_mark_labels;
 };
 
 /* This is the endianness of the current section.  */
@@ -174,6 +182,10 @@ static struct hash_control *alias_name_hash;
 static struct hash_control *secalias_hash;
 static struct hash_control *secalias_name_hash;
 
+/* List of chars besides those in app.c:symbol_chars that can start an
+   operand.  Used to prevent the scrubber eating vital white-space.  */
+const char ia64_symbol_chars[] = "@?";
+
 /* Characters which always start a comment.  */
 const char comment_chars[] = "";
 
@@ -182,7 +194,7 @@ const char line_comment_chars[] = "#";
 
 /* Characters which may be used to separate multiple commands on a
    single line.  */
-const char line_separator_chars[] = ";";
+const char line_separator_chars[] = ";{}";
 
 /* Characters which are used to indicate an exponent in a floating
    point number.  */
@@ -221,6 +233,21 @@ static struct
        that are predicatable.  */
     expressionS qp;
 
+    /* Optimize for which CPU.  */
+    enum
+      {
+       itanium1,
+       itanium2
+      } tune;
+
+    /* What to do when hint.b is used.  */
+    enum
+      {
+       hint_b_error,
+       hint_b_warning,
+       hint_b_ok
+      } hint_b;
+
     unsigned int
       manual_bundling : 1,
       debug_dv: 1,
@@ -231,6 +258,13 @@ static struct
       auto_align : 1,
       keep_pending_output : 1;
 
+    /* What to do when something is wrong with unwind directives.  */
+    enum
+      {
+       unwind_check_warning,
+       unwind_check_error
+      } unwind_check;
+
     /* Each bundle consists of up to three instructions.  We keep
        track of four most recent instructions so we can correctly set
        the end_of_insn_group for the last instruction in a bundle.  */
@@ -241,7 +275,8 @@ static struct
        unsigned int
          end_of_insn_group : 1,
          manual_bundling_on : 1,
-         manual_bundling_off : 1;
+         manual_bundling_off : 1,
+         loc_directive_seen : 1;
        signed char user_template;      /* user-selected template, if any */
        unsigned char qp_regno;         /* qualifying predicate */
        /* This duplicates a good fraction of "struct fix" but we
@@ -291,31 +326,27 @@ static struct
                                       the current DV-checking block.  */
     int maxpaths;                   /* size currently allocated for
                                       entry_labels */
-    /* Support for hardware errata workarounds.  */
-
-    /* Record data about the last three insn groups.  */
-    struct group
-    {
-      /* B-step workaround.
-        For each predicate register, this is set if the corresponding insn
-        group conditionally sets this register with one of the affected
-        instructions.  */
-      int p_reg_set[64];
-      /* B-step workaround.
-        For each general register, this is set if the corresponding insn
-        a) is conditional one one of the predicate registers for which
-           P_REG_SET is 1 in the corresponding entry of the previous group,
-        b) sets this general register with one of the affected
-           instructions.  */
-      int g_reg_set_conditionally[128];
-    } last_groups[3];
-    int group_idx;
 
     int pointer_size;       /* size in bytes of a pointer */
     int pointer_size_shift; /* shift size of a pointer for alignment */
   }
 md;
 
+/* These are not const, because they are modified to MMI for non-itanium1
+   targets below.  */
+/* MFI bundle of nops.  */
+static unsigned char le_nop[16] =
+{
+  0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
+};
+/* MFI bundle of nops with stop-bit.  */
+static unsigned char le_nop_stop[16] =
+{
+  0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
+};
+
 /* application registers:  */
 
 #define AR_K0          0
@@ -513,10 +544,10 @@ pseudo_func[] =
     { "segrel",        PSEUDO_FUNC_RELOC, { 0 } },
     { "tprel", PSEUDO_FUNC_RELOC, { 0 } },
     { "ltv",   PSEUDO_FUNC_RELOC, { 0 } },
-    { "", 0, { 0 } },  /* placeholder for FUNC_LT_FPTR_RELATIVE */
-    { "", 0, { 0 } },  /* placeholder for FUNC_LT_DTP_MODULE */
-    { "", 0, { 0 } },  /* placeholder for FUNC_LT_DTP_RELATIVE */
-    { "", 0, { 0 } },  /* placeholder for FUNC_LT_TP_RELATIVE */
+    { NULL, 0, { 0 } },        /* placeholder for FUNC_LT_FPTR_RELATIVE */
+    { NULL, 0, { 0 } },        /* placeholder for FUNC_LT_DTP_MODULE */
+    { NULL, 0, { 0 } },        /* placeholder for FUNC_LT_DTP_RELATIVE */
+    { NULL, 0, { 0 } },        /* placeholder for FUNC_LT_TP_RELATIVE */
     { "iplt",  PSEUDO_FUNC_RELOC, { 0 } },
 
     /* mbtype4 constants:  */
@@ -563,7 +594,7 @@ static const bfd_vma nop[IA64_NUM_UNITS] =
     0x0008000000LL,    /* M-unit nop */
     0x4000000000LL,    /* B-unit nop */
     0x0008000000LL,    /* F-unit nop */
-    0x0008000000LL,    /* L-"unit" nop */
+    0x0000000000LL,    /* L-"unit" nop immediate */
     0x0008000000LL,    /* X-unit nop */
   };
 
@@ -634,7 +665,17 @@ static struct gr {
   unsigned known:1;
   int path;
   valueT value;
-} gr_values[128] = {{ 1, 0, 0 }};
+} gr_values[128] = {
+  {
+    1,
+#ifdef INT_MAX
+    INT_MAX,
+#else
+    (((1 << (8 * sizeof(gr_values->path) - 2)) - 1) << 1) + 1,
+#endif
+    0
+  }
+};
 
 /* Remember the alignment frag.  */
 static fragS *align_frag;
@@ -655,8 +696,6 @@ typedef struct unw_rec_list {
   unwind_record r;
   unsigned long slot_number;
   fragS *slot_frag;
-  unsigned long next_slot_number;
-  fragS *next_slot_frag;
   struct unw_rec_list *next;
 } unw_rec_list;
 
@@ -671,6 +710,12 @@ typedef struct label_prologue_count
   unsigned int prologue_count;
 } label_prologue_count;
 
+typedef struct proc_pending
+{
+  symbolS *sym;
+  struct proc_pending *next;
+} proc_pending;
+
 static struct
 {
   /* Maintain a list of unwind entries for the current function.  */
@@ -682,8 +727,7 @@ static struct
   unw_rec_list *current_entry;
 
   /* These are used to create the unwind table entry for this function.  */
-  symbolS *proc_start;
-  symbolS *proc_end;
+  proc_pending proc_pending;
   symbolS *info;               /* pointer to unwind info */
   symbolS *personality_routine;
   segT saved_text_seg;
@@ -691,13 +735,25 @@ static struct
   unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */
 
   /* TRUE if processing unwind directives in a prologue region.  */
-  int prologue;
-  int prologue_mask;
+  unsigned int prologue : 1;
+  unsigned int prologue_mask : 4;
+  unsigned int prologue_gr : 7;
+  unsigned int body : 1;
+  unsigned int insn : 1;
   unsigned int prologue_count; /* number of .prologues seen so far */
   /* Prologue counts at previous .label_state directives.  */
   struct label_prologue_count * saved_prologue_counts;
+
+  /* List of split up .save-s.  */
+  unw_p_record *pending_saves;
 } unwind;
 
+/* The input value is a negated offset from psp, and specifies an address
+   psp - offset.  The encoded value is psp + 16 - (4 * offset).  Thus we
+   must add 16 and divide by 4 to get the encoded value.  */
+
+#define ENCODED_PSP_OFFSET(OFFSET) (((OFFSET) + 16) / 4)
+
 typedef void (*vbyte_func) PARAMS ((int, char *, char *));
 
 /* Forward declarations:  */
@@ -711,11 +767,9 @@ static void dot_proc PARAMS ((int));
 static void dot_fframe PARAMS ((int));
 static void dot_vframe PARAMS ((int));
 static void dot_vframesp PARAMS ((int));
-static void dot_vframepsp PARAMS ((int));
 static void dot_save PARAMS ((int));
 static void dot_restore PARAMS ((int));
 static void dot_restorereg PARAMS ((int));
-static void dot_restorereg_p PARAMS ((int));
 static void dot_handlerdata  PARAMS ((int));
 static void dot_unwentry PARAMS ((int));
 static void dot_altrp PARAMS ((int));
@@ -727,8 +781,6 @@ static void dot_savegf PARAMS ((int));
 static void dot_spill PARAMS ((int));
 static void dot_spillreg PARAMS ((int));
 static void dot_spillmem PARAMS ((int));
-static void dot_spillreg_p PARAMS ((int));
-static void dot_spillmem_p PARAMS ((int));
 static void dot_label_state PARAMS ((int));
 static void dot_copy_state PARAMS ((int));
 static void dot_unwabi PARAMS ((int));
@@ -743,7 +795,7 @@ static void dot_byteorder PARAMS ((int));
 static void dot_psr PARAMS ((int));
 static void dot_alias PARAMS ((int));
 static void dot_ln PARAMS ((int));
-static char *parse_section_name PARAMS ((void));
+static void cross_section PARAMS ((int ref, void (*cons) PARAMS((int)), int ua));
 static void dot_xdata PARAMS ((int));
 static void stmt_float_cons PARAMS ((int));
 static void stmt_cons_ua PARAMS ((int));
@@ -758,16 +810,15 @@ static void dot_serialize PARAMS ((int));
 static void dot_dv_mode PARAMS ((int));
 static void dot_entry PARAMS ((int));
 static void dot_mem_offset PARAMS ((int));
-static void add_unwind_entry PARAMS((unw_rec_list *ptr));
+static void add_unwind_entry PARAMS((unw_rec_list *, int));
 static symbolS *declare_register PARAMS ((const char *name, int regnum));
 static void declare_register_set PARAMS ((const char *, int, int));
 static unsigned int operand_width PARAMS ((enum ia64_opnd));
 static enum operand_match_result operand_match PARAMS ((const struct ia64_opcode *idesc,
                                                        int index,
                                                        expressionS *e));
-static int parse_operand PARAMS ((expressionS *e));
+static int parse_operand PARAMS ((expressionS *, int));
 static struct ia64_opcode * parse_operands PARAMS ((struct ia64_opcode *));
-static int errata_nop_necessary_p PARAMS ((struct slot *, enum ia64_unit));
 static void build_insn PARAMS ((struct slot *, bfd_vma *));
 static void emit_one_bundle PARAMS ((void));
 static void fix_insn PARAMS ((fixS *, const struct ia64_operand *, valueT));
@@ -887,15 +938,11 @@ static unw_rec_list *output_unwabi PARAMS ((unsigned long, unsigned long));
 static unw_rec_list *output_epilogue PARAMS ((unsigned long));
 static unw_rec_list *output_label_state PARAMS ((unsigned long));
 static unw_rec_list *output_copy_state PARAMS ((unsigned long));
-static unw_rec_list *output_spill_psprel PARAMS ((unsigned int, unsigned int, unsigned int));
-static unw_rec_list *output_spill_sprel PARAMS ((unsigned int, unsigned int, unsigned int));
-static unw_rec_list *output_spill_psprel_p PARAMS ((unsigned int, unsigned int, unsigned int,
+static unw_rec_list *output_spill_psprel PARAMS ((unsigned int, unsigned int, unsigned int,
                                                    unsigned int));
-static unw_rec_list *output_spill_sprel_p PARAMS ((unsigned int, unsigned int, unsigned int,
+static unw_rec_list *output_spill_sprel PARAMS ((unsigned int, unsigned int, unsigned int,
                                                   unsigned int));
 static unw_rec_list *output_spill_reg PARAMS ((unsigned int, unsigned int, unsigned int,
-                                              unsigned int));
-static unw_rec_list *output_spill_reg_p PARAMS ((unsigned int, unsigned int, unsigned int,
                                                 unsigned int, unsigned int));
 static void process_one_record PARAMS ((unw_rec_list *, vbyte_func));
 static void process_unw_records PARAMS ((unw_rec_list *, vbyte_func));
@@ -906,8 +953,9 @@ static unsigned long slot_index PARAMS ((unsigned long, fragS *,
                                         int));
 static unw_rec_list *optimize_unw_records PARAMS ((unw_rec_list *));
 static void fixup_unw_records PARAMS ((unw_rec_list *, int));
-static int convert_expr_to_ab_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
-static int convert_expr_to_xy_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
+static int parse_predicate_and_operand PARAMS ((expressionS *, unsigned *, const char *));
+static void convert_expr_to_ab_reg PARAMS ((const expressionS *, unsigned int *, unsigned int *, const char *, int));
+static void convert_expr_to_xy_reg PARAMS ((const expressionS *, unsigned int *, unsigned int *, const char *, int));
 static unsigned int get_saved_prologue_count PARAMS ((unsigned long));
 static void save_prologue_count PARAMS ((unsigned long, unsigned int));
 static void free_saved_prologue_counts PARAMS ((void));
@@ -996,12 +1044,6 @@ ia64_elf_section_type (str, len)
   if (STREQ ("unwind"))
     return SHT_IA_64_UNWIND;
 
-  if (STREQ ("init_array"))
-    return SHT_INIT_ARRAY;
-
-  if (STREQ ("fini_array"))
-    return SHT_FINI_ARRAY;
-
   return -1;
 #undef STREQ
 }
@@ -1042,6 +1084,7 @@ ia64_flush_insns ()
   segT saved_seg;
   subsegT saved_subseg;
   unw_rec_list *ptr;
+  bfd_boolean mark;
 
   if (!md.last_text_seg)
     return;
@@ -1055,18 +1098,23 @@ ia64_flush_insns ()
     emit_one_bundle ();                /* force out queued instructions */
 
   /* In case there are labels following the last instruction, resolve
-     those now:  */
+     those now.  */
+  mark = FALSE;
   for (lfix = CURR_SLOT.label_fixups; lfix; lfix = lfix->next)
     {
-      S_SET_VALUE (lfix->sym, frag_now_fix ());
-      symbol_set_frag (lfix->sym, frag_now);
+      symbol_set_value_now (lfix->sym);
+      mark |= lfix->dw2_mark_labels;
     }
-  CURR_SLOT.label_fixups = 0;
-  for (lfix = CURR_SLOT.tag_fixups; lfix; lfix = lfix->next)
+  if (mark)
     {
-      S_SET_VALUE (lfix->sym, frag_now_fix ());
-      symbol_set_frag (lfix->sym, frag_now);
+      dwarf2_where (&CURR_SLOT.debug_line);
+      CURR_SLOT.debug_line.flags |= DWARF2_FLAG_BASIC_BLOCK;
+      dwarf2_gen_line_info (frag_now_fix (), &CURR_SLOT.debug_line);
     }
+  CURR_SLOT.label_fixups = 0;
+
+  for (lfix = CURR_SLOT.tag_fixups; lfix; lfix = lfix->next)
+    symbol_set_value_now (lfix->sym);
   CURR_SLOT.tag_fixups = 0;
 
   /* In case there are unwind directives following the last instruction,
@@ -1137,7 +1185,7 @@ ia64_cons_align (nbytes)
 }
 
 /* Output COUNT bytes to a memory location.  */
-static unsigned char *vbyte_mem_ptr = NULL;
+static char *vbyte_mem_ptr = NULL;
 
 void
 output_vbyte_mem (count, ptr, comment)
@@ -1314,7 +1362,7 @@ output_P4_format (f, imask, imask_size)
      unsigned long imask_size;
 {
   imask[0] = UNW_P4;
-  (*f) (imask_size, imask, NULL);
+  (*f) (imask_size, (char *) imask, NULL);
 }
 
 static void
@@ -1700,6 +1748,55 @@ output_X4_format (f, qp, ab, reg, x, y, treg, t)
   (*f) (count, bytes, NULL);
 }
 
+/* This function checks whether there are any outstanding .save-s and
+   discards them if so.  */
+
+static void
+check_pending_save (void)
+{
+  if (unwind.pending_saves)
+    {
+      unw_rec_list *cur, *prev;
+
+      as_warn ("Previous .save incomplete");
+      for (cur = unwind.list, prev = NULL; cur; )
+       if (&cur->r.record.p == unwind.pending_saves)
+         {
+           if (prev)
+             prev->next = cur->next;
+           else
+             unwind.list = cur->next;
+           if (cur == unwind.tail)
+             unwind.tail = prev;
+           if (cur == unwind.current_entry)
+             unwind.current_entry = cur->next;
+           /* Don't free the first discarded record, it's being used as
+              terminator for (currently) br_gr and gr_gr processing, and
+              also prevents leaving a dangling pointer to it in its
+              predecessor.  */
+           cur->r.record.p.grmask = 0;
+           cur->r.record.p.brmask = 0;
+           cur->r.record.p.frmask = 0;
+           prev = cur->r.record.p.next;
+           cur->r.record.p.next = NULL;
+           cur = prev;
+           break;
+         }
+       else
+         {
+           prev = cur;
+           cur = cur->next;
+         }
+      while (cur)
+       {
+         prev = cur;
+         cur = cur->r.record.p.next;
+         free (prev);
+       }
+      unwind.pending_saves = NULL;
+    }
+}
+
 /* This function allocates a record list structure, and initializes fields.  */
 
 static unw_rec_list *
@@ -1707,11 +1804,9 @@ alloc_record (unw_record_type t)
 {
   unw_rec_list *ptr;
   ptr = xmalloc (sizeof (*ptr));
-  ptr->next = NULL;
+  memset (ptr, 0, sizeof (*ptr));
   ptr->slot_number = SLOT_NUM_NOT_SET;
   ptr->r.type = t;
-  ptr->next_slot_number = 0;
-  ptr->next_slot_frag = 0;
   return ptr;
 }
 
@@ -1773,7 +1868,7 @@ output_psp_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (psp_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -1782,7 +1877,7 @@ output_psp_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (psp_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -1798,7 +1893,7 @@ output_rp_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (rp_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -1807,7 +1902,7 @@ output_rp_br (br)
      unsigned int br;
 {
   unw_rec_list *ptr = alloc_record (rp_br);
-  ptr->r.record.p.br = br;
+  ptr->r.record.p.r.br = br;
   return ptr;
 }
 
@@ -1816,7 +1911,7 @@ output_rp_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rp_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1825,7 +1920,7 @@ output_rp_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rp_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -1841,7 +1936,7 @@ output_pfs_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (pfs_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -1850,7 +1945,7 @@ output_pfs_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (pfs_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1859,7 +1954,7 @@ output_pfs_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (pfs_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -1875,7 +1970,7 @@ output_preds_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (preds_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -1884,7 +1979,7 @@ output_preds_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (preds_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1893,7 +1988,7 @@ output_preds_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (preds_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -1902,8 +1997,24 @@ output_fr_mem (mask)
      unsigned int mask;
 {
   unw_rec_list *ptr = alloc_record (fr_mem);
-  ptr->r.record.p.rmask = mask;
-  return ptr;
+  unw_rec_list *cur = ptr;
+
+  ptr->r.record.p.frmask = mask;
+  unwind.pending_saves = &ptr->r.record.p;
+  for (;;)
+    {
+      unw_rec_list *prev = cur;
+
+      /* Clear least significant set bit.  */
+      mask &= ~(mask & (~mask + 1));
+      if (!mask)
+       return ptr;
+      cur = alloc_record (fr_mem);
+      cur->r.record.p.frmask = mask;
+      /* Retain only least significant bit.  */
+      prev->r.record.p.frmask ^= mask;
+      prev->r.record.p.next = cur;
+    }
 }
 
 static unw_rec_list *
@@ -1912,9 +2023,39 @@ output_frgr_mem (gr_mask, fr_mask)
      unsigned int fr_mask;
 {
   unw_rec_list *ptr = alloc_record (frgr_mem);
-  ptr->r.record.p.grmask = gr_mask;
-  ptr->r.record.p.frmask = fr_mask;
-  return ptr;
+  unw_rec_list *cur = ptr;
+
+  unwind.pending_saves = &cur->r.record.p;
+  cur->r.record.p.frmask = fr_mask;
+  while (fr_mask)
+    {
+      unw_rec_list *prev = cur;
+
+      /* Clear least significant set bit.  */
+      fr_mask &= ~(fr_mask & (~fr_mask + 1));
+      if (!gr_mask && !fr_mask)
+       return ptr;
+      cur = alloc_record (frgr_mem);
+      cur->r.record.p.frmask = fr_mask;
+      /* Retain only least significant bit.  */
+      prev->r.record.p.frmask ^= fr_mask;
+      prev->r.record.p.next = cur;
+    }
+  cur->r.record.p.grmask = gr_mask;
+  for (;;)
+    {
+      unw_rec_list *prev = cur;
+
+      /* Clear least significant set bit.  */
+      gr_mask &= ~(gr_mask & (~gr_mask + 1));
+      if (!gr_mask)
+       return ptr;
+      cur = alloc_record (frgr_mem);
+      cur->r.record.p.grmask = gr_mask;
+      /* Retain only least significant bit.  */
+      prev->r.record.p.grmask ^= gr_mask;
+      prev->r.record.p.next = cur;
+    }
 }
 
 static unw_rec_list *
@@ -1923,9 +2064,27 @@ output_gr_gr (mask, reg)
      unsigned int reg;
 {
   unw_rec_list *ptr = alloc_record (gr_gr);
+  unw_rec_list *cur = ptr;
+
   ptr->r.record.p.grmask = mask;
-  ptr->r.record.p.gr = reg;
-  return ptr;
+  ptr->r.record.p.r.gr = reg;
+  unwind.pending_saves = &ptr->r.record.p;
+  for (;;)
+    {
+      unw_rec_list *prev = cur;
+
+      /* Clear least significant set bit.  */
+      mask &= ~(mask & (~mask + 1));
+      if (!mask)
+       return ptr;
+      cur = alloc_record (gr_gr);
+      cur->r.record.p.grmask = mask;
+      /* Indicate this record shouldn't be output.  */
+      cur->r.record.p.r.gr = REG_NUM;
+      /* Retain only least significant bit.  */
+      prev->r.record.p.grmask ^= mask;
+      prev->r.record.p.next = cur;
+    }
 }
 
 static unw_rec_list *
@@ -1933,27 +2092,77 @@ output_gr_mem (mask)
      unsigned int mask;
 {
   unw_rec_list *ptr = alloc_record (gr_mem);
-  ptr->r.record.p.rmask = mask;
-  return ptr;
+  unw_rec_list *cur = ptr;
+
+  ptr->r.record.p.grmask = mask;
+  unwind.pending_saves = &ptr->r.record.p;
+  for (;;)
+    {
+      unw_rec_list *prev = cur;
+
+      /* Clear least significant set bit.  */
+      mask &= ~(mask & (~mask + 1));
+      if (!mask)
+       return ptr;
+      cur = alloc_record (gr_mem);
+      cur->r.record.p.grmask = mask;
+      /* Retain only least significant bit.  */
+      prev->r.record.p.grmask ^= mask;
+      prev->r.record.p.next = cur;
+    }
 }
 
 static unw_rec_list *
 output_br_mem (unsigned int mask)
 {
   unw_rec_list *ptr = alloc_record (br_mem);
+  unw_rec_list *cur = ptr;
+
   ptr->r.record.p.brmask = mask;
-  return ptr;
+  unwind.pending_saves = &ptr->r.record.p;
+  for (;;)
+    {
+      unw_rec_list *prev = cur;
+
+      /* Clear least significant set bit.  */
+      mask &= ~(mask & (~mask + 1));
+      if (!mask)
+       return ptr;
+      cur = alloc_record (br_mem);
+      cur->r.record.p.brmask = mask;
+      /* Retain only least significant bit.  */
+      prev->r.record.p.brmask ^= mask;
+      prev->r.record.p.next = cur;
+    }
 }
 
 static unw_rec_list *
-output_br_gr (save_mask, reg)
-     unsigned int save_mask;
+output_br_gr (mask, reg)
+     unsigned int mask;
      unsigned int reg;
 {
   unw_rec_list *ptr = alloc_record (br_gr);
-  ptr->r.record.p.brmask = save_mask;
-  ptr->r.record.p.gr = reg;
-  return ptr;
+  unw_rec_list *cur = ptr;
+
+  ptr->r.record.p.brmask = mask;
+  ptr->r.record.p.r.gr = reg;
+  unwind.pending_saves = &ptr->r.record.p;
+  for (;;)
+    {
+      unw_rec_list *prev = cur;
+
+      /* Clear least significant set bit.  */
+      mask &= ~(mask & (~mask + 1));
+      if (!mask)
+       return ptr;
+      cur = alloc_record (br_gr);
+      cur->r.record.p.brmask = mask;
+      /* Indicate this record shouldn't be output.  */
+      cur->r.record.p.r.gr = REG_NUM;
+      /* Retain only least significant bit.  */
+      prev->r.record.p.brmask ^= mask;
+      prev->r.record.p.next = cur;
+    }
 }
 
 static unw_rec_list *
@@ -1961,7 +2170,7 @@ output_spill_base (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (spill_base);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1977,7 +2186,7 @@ output_unat_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (unat_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -1986,7 +2195,7 @@ output_unat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (unat_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1995,7 +2204,7 @@ output_unat_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (unat_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -2011,7 +2220,7 @@ output_lc_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (lc_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -2020,7 +2229,7 @@ output_lc_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (lc_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2029,7 +2238,7 @@ output_lc_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (lc_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -2045,7 +2254,7 @@ output_fpsr_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (fpsr_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -2054,7 +2263,7 @@ output_fpsr_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (fpsr_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2063,7 +2272,7 @@ output_fpsr_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (fpsr_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -2086,7 +2295,7 @@ output_priunat_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (priunat_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -2095,7 +2304,7 @@ output_priunat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (priunat_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2104,7 +2313,7 @@ output_priunat_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (priunat_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -2120,7 +2329,7 @@ output_bsp_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (bsp_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -2129,7 +2338,7 @@ output_bsp_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bsp_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2138,7 +2347,7 @@ output_bsp_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bsp_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -2154,7 +2363,7 @@ output_bspstore_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (bspstore_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -2163,7 +2372,7 @@ output_bspstore_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bspstore_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2172,7 +2381,7 @@ output_bspstore_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bspstore_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -2188,7 +2397,7 @@ output_rnat_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (rnat_gr);
-  ptr->r.record.p.gr = gr;
+  ptr->r.record.p.r.gr = gr;
   return ptr;
 }
 
@@ -2197,7 +2406,7 @@ output_rnat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rnat_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2206,7 +2415,7 @@ output_rnat_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rnat_sprel);
-  ptr->r.record.p.spoff = offset / 4;
+  ptr->r.record.p.off.sp = offset / 4;
   return ptr;
 }
 
@@ -2246,88 +2455,47 @@ output_copy_state (unsigned long label)
 }
 
 static unw_rec_list *
-output_spill_psprel (ab, reg, offset)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int offset;
-{
-  unw_rec_list *ptr = alloc_record (spill_psprel);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.pspoff = offset / 4;
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_sprel (ab, reg, offset)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int offset;
-{
-  unw_rec_list *ptr = alloc_record (spill_sprel);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.spoff = offset / 4;
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_psprel_p (ab, reg, offset, predicate)
+output_spill_psprel (ab, reg, offset, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int offset;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_psprel_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_psprel_p : spill_psprel);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
-  ptr->r.record.x.pspoff = offset / 4;
+  ptr->r.record.x.where.pspoff = ENCODED_PSP_OFFSET (offset);
   ptr->r.record.x.qp = predicate;
   return ptr;
 }
 
 static unw_rec_list *
-output_spill_sprel_p (ab, reg, offset, predicate)
+output_spill_sprel (ab, reg, offset, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int offset;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_sprel_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_sprel_p : spill_sprel);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
-  ptr->r.record.x.spoff = offset / 4;
+  ptr->r.record.x.where.spoff = offset / 4;
   ptr->r.record.x.qp = predicate;
   return ptr;
 }
 
 static unw_rec_list *
-output_spill_reg (ab, reg, targ_reg, xy)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int targ_reg;
-     unsigned int xy;
-{
-  unw_rec_list *ptr = alloc_record (spill_reg);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.treg = targ_reg;
-  ptr->r.record.x.xy = xy;
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_reg_p (ab, reg, targ_reg, xy, predicate)
+output_spill_reg (ab, reg, targ_reg, xy, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int targ_reg;
      unsigned int xy;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_reg_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_reg_p : spill_reg);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
-  ptr->r.record.x.treg = targ_reg;
+  ptr->r.record.x.where.reg = targ_reg;
   ptr->r.record.x.xy = xy;
   ptr->r.record.x.qp = predicate;
   return ptr;
@@ -2341,7 +2509,7 @@ process_one_record (ptr, f)
      unw_rec_list *ptr;
      vbyte_func f;
 {
-  unsigned long fr_mask, gr_mask;
+  unsigned int fr_mask, gr_mask;
 
   switch (ptr->r.type)
     {
@@ -2407,13 +2575,13 @@ process_one_record (ptr, f)
     case bsp_gr:
     case bspstore_gr:
     case rnat_gr:
-      output_P3_format (f, ptr->r.type, ptr->r.record.p.gr);
+      output_P3_format (f, ptr->r.type, ptr->r.record.p.r.gr);
       break;
     case rp_br:
-      output_P3_format (f, rp_br, ptr->r.record.p.br);
+      output_P3_format (f, rp_br, ptr->r.record.p.r.br);
       break;
     case psp_sprel:
-      output_P7_format (f, psp_sprel, ptr->r.record.p.spoff, 0);
+      output_P7_format (f, psp_sprel, ptr->r.record.p.off.sp, 0);
       break;
     case rp_when:
     case pfs_when:
@@ -2430,7 +2598,7 @@ process_one_record (ptr, f)
     case lc_psprel:
     case fpsr_psprel:
     case spill_base:
-      output_P7_format (f, ptr->r.type, ptr->r.record.p.pspoff, 0);
+      output_P7_format (f, ptr->r.type, ptr->r.record.p.off.psp, 0);
       break;
     case rp_sprel:
     case pfs_sprel:
@@ -2442,13 +2610,29 @@ process_one_record (ptr, f)
     case bsp_sprel:
     case bspstore_sprel:
     case rnat_sprel:
-      output_P8_format (f, ptr->r.type, ptr->r.record.p.spoff);
+      output_P8_format (f, ptr->r.type, ptr->r.record.p.off.sp);
       break;
     case gr_gr:
-      output_P9_format (f, ptr->r.record.p.grmask, ptr->r.record.p.gr);
+      if (ptr->r.record.p.r.gr < REG_NUM)
+       {
+         const unw_rec_list *cur = ptr;
+
+         gr_mask = cur->r.record.p.grmask;
+         while ((cur = cur->r.record.p.next) != NULL)
+           gr_mask |= cur->r.record.p.grmask;
+         output_P9_format (f, gr_mask, ptr->r.record.p.r.gr);
+       }
       break;
     case br_gr:
-      output_P2_format (f, ptr->r.record.p.brmask, ptr->r.record.p.gr);
+      if (ptr->r.record.p.r.gr < REG_NUM)
+       {
+         const unw_rec_list *cur = ptr;
+
+         gr_mask = cur->r.record.p.brmask;
+         while ((cur = cur->r.record.p.next) != NULL)
+           gr_mask |= cur->r.record.p.brmask;
+         output_P2_format (f, gr_mask, ptr->r.record.p.r.gr);
+       }
       break;
     case spill_mask:
       as_bad ("spill_mask record unimplemented.");
@@ -2464,7 +2648,7 @@ process_one_record (ptr, f)
     case bsp_psprel:
     case bspstore_psprel:
     case rnat_psprel:
-      output_P8_format (f, ptr->r.type, ptr->r.record.p.pspoff);
+      output_P8_format (f, ptr->r.type, ptr->r.record.p.off.psp);
       break;
     case unwabi:
       output_P10_format (f, ptr->r.record.p.abi, ptr->r.record.p.context);
@@ -2479,32 +2663,32 @@ process_one_record (ptr, f)
     case spill_psprel:
       output_X1_format (f, ptr->r.type, ptr->r.record.x.ab,
                        ptr->r.record.x.reg, ptr->r.record.x.t,
-                       ptr->r.record.x.pspoff);
+                       ptr->r.record.x.where.pspoff);
       break;
     case spill_sprel:
       output_X1_format (f, ptr->r.type, ptr->r.record.x.ab,
                        ptr->r.record.x.reg, ptr->r.record.x.t,
-                       ptr->r.record.x.spoff);
+                       ptr->r.record.x.where.spoff);
       break;
     case spill_reg:
       output_X2_format (f, ptr->r.record.x.ab, ptr->r.record.x.reg,
                        ptr->r.record.x.xy >> 1, ptr->r.record.x.xy,
-                       ptr->r.record.x.treg, ptr->r.record.x.t);
+                       ptr->r.record.x.where.reg, ptr->r.record.x.t);
       break;
     case spill_psprel_p:
       output_X3_format (f, ptr->r.type, ptr->r.record.x.qp,
                        ptr->r.record.x.ab, ptr->r.record.x.reg,
-                       ptr->r.record.x.t, ptr->r.record.x.pspoff);
+                       ptr->r.record.x.t, ptr->r.record.x.where.pspoff);
       break;
     case spill_sprel_p:
       output_X3_format (f, ptr->r.type, ptr->r.record.x.qp,
                        ptr->r.record.x.ab, ptr->r.record.x.reg,
-                       ptr->r.record.x.t, ptr->r.record.x.spoff);
+                       ptr->r.record.x.t, ptr->r.record.x.where.spoff);
       break;
     case spill_reg_p:
       output_X4_format (f, ptr->r.record.x.qp, ptr->r.record.x.ab,
                        ptr->r.record.x.reg, ptr->r.record.x.xy >> 1,
-                       ptr->r.record.x.xy, ptr->r.record.x.treg,
+                       ptr->r.record.x.xy, ptr->r.record.x.where.reg,
                        ptr->r.record.x.t);
       break;
     default:
@@ -2535,6 +2719,28 @@ calc_record_size (list)
   return vbyte_count;
 }
 
+/* Return the number of bits set in the input value.
+   Perhaps this has a better place...  */
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# define popcount __builtin_popcount
+#else
+static int
+popcount (unsigned x)
+{
+  static const unsigned char popcnt[16] =
+    {
+      0, 1, 1, 2,
+      1, 2, 2, 3,
+      1, 2, 2, 3,
+      2, 3, 3, 4
+    };
+
+  if (x < NELEMS (popcnt))
+    return popcnt[x];
+  return popcnt[x % NELEMS (popcnt)] + popcount (x / NELEMS (popcnt));
+}
+#endif
+
 /* Update IMASK bitmask to reflect the fact that one or more registers
    of type TYPE are saved starting at instruction with index T.  If N
    bits are set in REGMASK, it is assumed that instructions T through
@@ -2666,6 +2872,26 @@ slot_index (slot_addr, slot_frag, first_addr, first_frag, before_relax)
       /* Move to the beginning of the next frag.  */
       first_frag = first_frag->fr_next;
       first_addr = (unsigned long) &first_frag->fr_literal;
+
+      /* This can happen if there is section switching in the middle of a
+        function, causing the frag chain for the function to be broken.  */
+      if (first_frag == NULL)
+       {
+         /* We get six warnings for one problem, because of the loop in
+            fixup_unw_records, and because fixup_unw_records is called 3
+            times: once before creating the variant frag, once to estimate
+            its size, and once to relax it.  This is unreasonable, so we use
+            a static var to make sure we only emit the warning once.  */
+         static int warned = 0;
+
+         if (!warned)
+           {
+             as_warn ("Corrupted unwind info due to unsupported section switching");
+             warned = 1;
+           }
+
+         return index;
+       }
     }
 
   /* Add in the used part of the last frag.  */
@@ -2747,7 +2973,13 @@ fixup_unw_records (list, before_relax)
            break;
          }
        case epilogue:
-         ptr->r.record.b.t = rlen - 1 - t;
+         if (t < rlen)
+           ptr->r.record.b.t = rlen - 1 - t;
+         else
+           /* This happens when a memory-stack-less procedure uses a
+              ".restore sp" directive at the end of a region to pop
+              the frame state.  */
+           ptr->r.record.b.t = 0;
          break;
 
        case mem_stack_f:
@@ -2778,7 +3010,7 @@ fixup_unw_records (list, before_relax)
        case frgr_mem:
          if (!region)
            {
-             as_bad ("frgr_mem record before region record!\n");
+             as_bad ("frgr_mem record before region record!");
              return;
            }
          region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask;
@@ -2789,25 +3021,25 @@ fixup_unw_records (list, before_relax)
        case fr_mem:
          if (!region)
            {
-             as_bad ("fr_mem record before region record!\n");
+             as_bad ("fr_mem record before region record!");
              return;
            }
-         region->r.record.r.mask.fr_mem |= ptr->r.record.p.rmask;
-         set_imask (region, ptr->r.record.p.rmask, t, 1);
+         region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask;
+         set_imask (region, ptr->r.record.p.frmask, t, 1);
          break;
        case gr_mem:
          if (!region)
            {
-             as_bad ("gr_mem record before region record!\n");
+             as_bad ("gr_mem record before region record!");
              return;
            }
-         region->r.record.r.mask.gr_mem |= ptr->r.record.p.rmask;
-         set_imask (region, ptr->r.record.p.rmask, t, 2);
+         region->r.record.r.mask.gr_mem |= ptr->r.record.p.grmask;
+         set_imask (region, ptr->r.record.p.grmask, t, 2);
          break;
        case br_mem:
          if (!region)
            {
-             as_bad ("br_mem record before region record!\n");
+             as_bad ("br_mem record before region record!");
              return;
            }
          region->r.record.r.mask.br_mem |= ptr->r.record.p.brmask;
@@ -2817,7 +3049,7 @@ fixup_unw_records (list, before_relax)
        case gr_gr:
          if (!region)
            {
-             as_bad ("gr_gr record before region record!\n");
+             as_bad ("gr_gr record before region record!");
              return;
            }
          set_imask (region, ptr->r.record.p.grmask, t, 2);
@@ -2825,7 +3057,7 @@ fixup_unw_records (list, before_relax)
        case br_gr:
          if (!region)
            {
-             as_bad ("br_gr record before region record!\n");
+             as_bad ("br_gr record before region record!");
              return;
            }
          set_imask (region, ptr->r.record.p.brmask, t, 3);
@@ -2856,8 +3088,11 @@ ia64_estimate_size_before_relax (fragS *frag,
   pad = len % md.pointer_size;
   if (pad != 0)
     len += md.pointer_size - pad;
-  /* Add 8 for the header + a pointer for the personality offset.  */
-  size = len + 8 + md.pointer_size;
+  /* Add 8 for the header.  */
+  size = len + 8;
+  /* Add a pointer for the personality offset.  */
+  if (frag->fr_offset)
+    size += md.pointer_size;
 
   /* fr_var carries the max_chars that we created the fragment with.
      We must, of course, have allocated enough memory earlier.  */
@@ -2884,8 +3119,11 @@ ia64_convert_frag (fragS *frag)
   pad = len % md.pointer_size;
   if (pad != 0)
     len += md.pointer_size - pad;
-  /* Add 8 for the header + a pointer for the personality offset.  */
-  size = len + 8 + md.pointer_size;
+  /* Add 8 for the header.  */
+  size = len + 8;
+  /* Add a pointer for the personality offset.  */
+  if (frag->fr_offset)
+    size += md.pointer_size;
 
   /* fr_var carries the max_chars that we created the fragment with.
      We must, of course, have allocated enough memory earlier.  */
@@ -2926,17 +3164,43 @@ ia64_convert_frag (fragS *frag)
 }
 
 static int
-convert_expr_to_ab_reg (e, ab, regp)
-     expressionS *e;
+parse_predicate_and_operand (e, qp, po)
+     expressionS * e;
+     unsigned * qp;
+     const char * po;
+{
+  int sep = parse_operand (e, ',');
+
+  *qp = e->X_add_number - REG_P;
+  if (e->X_op != O_register || *qp > 63)
+    {
+      as_bad ("First operand to .%s must be a predicate", po);
+      *qp = 0;
+    }
+  else if (*qp == 0)
+    as_warn ("Pointless use of p0 as first operand to .%s", po);
+  if (sep == ',')
+    sep = parse_operand (e, ',');
+  else
+    e->X_op = O_absent;
+  return sep;
+}
+
+static void
+convert_expr_to_ab_reg (e, ab, regp, po, n)
+     const expressionS *e;
      unsigned int *ab;
      unsigned int *regp;
+     const char * po;
+     int n;
 {
-  unsigned int reg;
+  unsigned int reg = e->X_add_number;
+
+  *ab = *regp = 0; /* Anything valid is good here.  */
 
   if (e->X_op != O_register)
-    return 0;
+    reg = REG_GR; /* Anything invalid is good here.  */
 
-  reg = e->X_add_number;
   if (reg >= (REG_GR + 4) && reg <= (REG_GR + 7))
     {
       *ab = 0;
@@ -2971,31 +3235,33 @@ convert_expr_to_ab_reg (e, ab, regp)
        case REG_AR + AR_LC:    *regp = 10; break;
 
        default:
-         return 0;
+         as_bad ("Operand %d to .%s must be a preserved register", n, po);
+         break;
        }
     }
-  return 1;
 }
 
-static int
-convert_expr_to_xy_reg (e, xy, regp)
-     expressionS *e;
+static void
+convert_expr_to_xy_reg (e, xy, regp, po, n)
+     const expressionS *e;
      unsigned int *xy;
      unsigned int *regp;
+     const char * po;
+     int n;
 {
-  unsigned int reg;
+  unsigned int reg = e->X_add_number;
 
-  if (e->X_op != O_register)
-    return 0;
+  *xy = *regp = 0; /* Anything valid is good here.  */
 
-  reg = e->X_add_number;
+  if (e->X_op != O_register)
+    reg = REG_GR; /* Anything invalid is good here.  */
 
-  if (/* reg >= REG_GR && */ reg <= (REG_GR + 127))
+  if (reg >= (REG_GR + 1) && reg <= (REG_GR + 127))
     {
       *xy = 0;
       *regp = reg - REG_GR;
     }
-  else if (reg >= REG_FR && reg <= (REG_FR + 127))
+  else if (reg >= (REG_FR + 2) && reg <= (REG_FR + 127))
     {
       *xy = 1;
       *regp = reg - REG_FR;
@@ -3006,8 +3272,7 @@ convert_expr_to_xy_reg (e, xy, regp)
       *regp = reg - REG_BR;
     }
   else
-    return -1;
-  return 1;
+    as_bad ("Operand %d to .%s must be a writable register", n, po);
 }
 
 static void
@@ -3022,17 +3287,31 @@ static void
 dot_radix (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  int radix;
+  char *radix;
+  int ch;
 
   SKIP_WHITESPACE ();
-  radix = *input_line_pointer++;
 
-  if (radix != 'C' && !is_end_of_line[(unsigned char) radix])
-    {
-      as_bad ("Radix `%c' unsupported", *input_line_pointer);
-      ignore_rest_of_line ();
-      return;
-    }
+  if (is_it_end_of_statement ())
+    return;
+  radix = input_line_pointer;
+  ch = get_symbol_end ();
+  ia64_canonicalize_symbol_name (radix);
+  if (strcasecmp (radix, "C"))
+    as_bad ("Radix `%s' unsupported or invalid", radix);
+  *input_line_pointer = ch;
+  demand_empty_rest_of_line ();
+}
+
+/* Helper function for .loc directives.  If the assembler is not generating
+   line number info, then we need to remember which instructions have a .loc
+   directive, and only call dwarf2_gen_line_info for those instructions.  */
+
+static void
+dot_loc (int x)
+{
+  CURR_SLOT.loc_directive_seen = 1;
+  dwarf2_directive_loc (x);
 }
 
 /* .sbss, .bss etc. are macros that expand into ".section SECNAME".  */
@@ -3043,19 +3322,112 @@ dot_special_section (which)
   set_section ((char *) special_section_name[which]);
 }
 
+/* Return -1 for warning and 0 for error.  */
+
+static int
+unwind_diagnostic (const char * region, const char *directive)
+{
+  if (md.unwind_check == unwind_check_warning)
+    {
+      as_warn (".%s outside of %s", directive, region);
+      return -1;
+    }
+  else
+    {
+      as_bad (".%s outside of %s", directive, region);
+      ignore_rest_of_line ();
+      return 0;
+    }
+}
+
+/* Return 1 if a directive is in a procedure, -1 if a directive isn't in
+   a procedure but the unwind directive check is set to warning, 0 if
+   a directive isn't in a procedure and the unwind directive check is set
+   to error.  */
+
+static int
+in_procedure (const char *directive)
+{
+  if (unwind.proc_pending.sym
+      && (!unwind.saved_text_seg || strcmp (directive, "endp") == 0))
+    return 1;
+  return unwind_diagnostic ("procedure", directive);
+}
+
+/* Return 1 if a directive is in a prologue, -1 if a directive isn't in
+   a prologue but the unwind directive check is set to warning, 0 if
+   a directive isn't in a prologue and the unwind directive check is set
+   to error.  */
+
+static int
+in_prologue (const char *directive)
+{
+  int in = in_procedure (directive);
+
+  if (in > 0 && !unwind.prologue)
+    in = unwind_diagnostic ("prologue", directive);
+  check_pending_save ();
+  return in;
+}
+
+/* Return 1 if a directive is in a body, -1 if a directive isn't in
+   a body but the unwind directive check is set to warning, 0 if
+   a directive isn't in a body and the unwind directive check is set
+   to error.  */
+
+static int
+in_body (const char *directive)
+{
+  int in = in_procedure (directive);
+
+  if (in > 0 && !unwind.body)
+    in = unwind_diagnostic ("body region", directive);
+  return in;
+}
+
 static void
-add_unwind_entry (ptr)
+add_unwind_entry (ptr, sep)
      unw_rec_list *ptr;
+     int sep;
 {
-  if (unwind.tail)
-    unwind.tail->next = ptr;
-  else
-    unwind.list = ptr;
-  unwind.tail = ptr;
+  if (ptr)
+    {
+      if (unwind.tail)
+       unwind.tail->next = ptr;
+      else
+       unwind.list = ptr;
+      unwind.tail = ptr;
+
+      /* The current entry can in fact be a chain of unwind entries.  */
+      if (unwind.current_entry == NULL)
+       unwind.current_entry = ptr;
+    }
 
   /* The current entry can in fact be a chain of unwind entries.  */
   if (unwind.current_entry == NULL)
     unwind.current_entry = ptr;
+
+  if (sep == ',')
+    {
+      /* Parse a tag permitted for the current directive.  */
+      int ch;
+
+      SKIP_WHITESPACE ();
+      ch = get_symbol_end ();
+      /* FIXME: For now, just issue a warning that this isn't implemented.  */
+      {
+       static int warned;
+
+       if (!warned)
+         {
+           warned = 1;
+           as_warn ("Tags on unwind pseudo-ops aren't supported, yet");
+         }
+      }
+      *input_line_pointer = ch;
+    }
+  if (sep != NOT_A_CHAR)
+    demand_empty_rest_of_line ();
 }
 
 static void
@@ -3063,13 +3435,19 @@ dot_fframe (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
+  int sep;
+
+  if (!in_prologue ("fframe"))
+    return;
 
-  parse_operand (&e);
+  sep = parse_operand (&e, ',');
 
   if (e.X_op != O_constant)
-    as_bad ("Operand to .fframe must be a constant");
-  else
-    add_unwind_entry (output_mem_stack_f (e.X_add_number));
+    {
+      as_bad ("First operand to .fframe must be a constant");
+      e.X_add_number = 0;
+    }
+  add_unwind_entry (output_mem_stack_f (e.X_add_number), sep);
 }
 
 static void
@@ -3078,150 +3456,163 @@ dot_vframe (dummy)
 {
   expressionS e;
   unsigned reg;
+  int sep;
+
+  if (!in_prologue ("vframe"))
+    return;
 
-  parse_operand (&e);
+  sep = parse_operand (&e, ',');
   reg = e.X_add_number - REG_GR;
-  if (e.X_op == O_register && reg < 128)
+  if (e.X_op != O_register || reg > 127)
     {
-      add_unwind_entry (output_mem_stack_v ());
-      if (! (unwind.prologue_mask & 2))
-       add_unwind_entry (output_psp_gr (reg));
+      as_bad ("First operand to .vframe must be a general register");
+      reg = 0;
     }
-  else
-    as_bad ("First operand to .vframe must be a general register");
+  add_unwind_entry (output_mem_stack_v (), sep);
+  if (! (unwind.prologue_mask & 2))
+    add_unwind_entry (output_psp_gr (reg), NOT_A_CHAR);
+  else if (reg != unwind.prologue_gr
+                 + (unsigned) popcount (unwind.prologue_mask & (-2 << 1)))
+    as_warn ("Operand of .vframe contradicts .prologue");
 }
 
 static void
-dot_vframesp (dummy)
-     int dummy ATTRIBUTE_UNUSED;
+dot_vframesp (psp)
+     int psp;
 {
   expressionS e;
+  int sep;
 
-  parse_operand (&e);
-  if (e.X_op == O_constant)
+  if (psp)
+    as_warn (".vframepsp is meaningless, assuming .vframesp was meant");
+
+  if (!in_prologue ("vframesp"))
+    return;
+
+  sep = parse_operand (&e, ',');
+  if (e.X_op != O_constant)
     {
-      add_unwind_entry (output_mem_stack_v ());
-      add_unwind_entry (output_psp_sprel (e.X_add_number));
+      as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
+      e.X_add_number = 0;
     }
-  else
-    as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
+  add_unwind_entry (output_mem_stack_v (), sep);
+  add_unwind_entry (output_psp_sprel (e.X_add_number), NOT_A_CHAR);
 }
 
 static void
-dot_vframepsp (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  expressionS e;
-
-  parse_operand (&e);
-  if (e.X_op == O_constant)
-    {
-      add_unwind_entry (output_mem_stack_v ());
-      add_unwind_entry (output_psp_sprel (e.X_add_number));
-    }
-  else
-    as_bad ("Operand to .vframepsp must be a constant (psp-relative offset)");
-}
-
-static void
-dot_save (dummy)
+dot_save (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
+  unsigned reg1, reg2;
   int sep;
-  int reg1, reg2;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    as_bad ("No second operand to .save");
-  sep = parse_operand (&e2);
+  if (!in_prologue ("save"))
+    return;
 
-  reg1 = e1.X_add_number;
-  reg2 = e2.X_add_number - REG_GR;
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    sep = parse_operand (&e2, ',');
+  else
+    e2.X_op = O_absent;
 
+  reg1 = e1.X_add_number;
   /* Make sure its a valid ar.xxx reg, OR its br0, aka 'rp'.  */
-  if (e1.X_op == O_register)
+  if (e1.X_op != O_register)
     {
-      if (e2.X_op == O_register && reg2 >= 0 && reg2 < 128)
-       {
-         switch (reg1)
-           {
-           case REG_AR + AR_BSP:
-             add_unwind_entry (output_bsp_when ());
-             add_unwind_entry (output_bsp_gr (reg2));
-             break;
-           case REG_AR + AR_BSPSTORE:
-             add_unwind_entry (output_bspstore_when ());
-             add_unwind_entry (output_bspstore_gr (reg2));
-             break;
-           case REG_AR + AR_RNAT:
-             add_unwind_entry (output_rnat_when ());
-             add_unwind_entry (output_rnat_gr (reg2));
-             break;
-           case REG_AR + AR_UNAT:
-             add_unwind_entry (output_unat_when ());
-             add_unwind_entry (output_unat_gr (reg2));
-             break;
-           case REG_AR + AR_FPSR:
-             add_unwind_entry (output_fpsr_when ());
-             add_unwind_entry (output_fpsr_gr (reg2));
-             break;
-           case REG_AR + AR_PFS:
-             add_unwind_entry (output_pfs_when ());
-             if (! (unwind.prologue_mask & 4))
-               add_unwind_entry (output_pfs_gr (reg2));
-             break;
-           case REG_AR + AR_LC:
-             add_unwind_entry (output_lc_when ());
-             add_unwind_entry (output_lc_gr (reg2));
-             break;
-           case REG_BR:
-             add_unwind_entry (output_rp_when ());
-             if (! (unwind.prologue_mask & 8))
-               add_unwind_entry (output_rp_gr (reg2));
-             break;
-           case REG_PR:
-             add_unwind_entry (output_preds_when ());
-             if (! (unwind.prologue_mask & 1))
-               add_unwind_entry (output_preds_gr (reg2));
-             break;
-           case REG_PRIUNAT:
-             add_unwind_entry (output_priunat_when_gr ());
-             add_unwind_entry (output_priunat_gr (reg2));
-             break;
-           default:
-             as_bad ("First operand not a valid register");
-           }
-       }
-      else
-       as_bad (" Second operand not a valid register");
+      as_bad ("First operand to .save not a register");
+      reg1 = REG_PR; /* Anything valid is good here.  */
+    }
+  reg2 = e2.X_add_number - REG_GR;
+  if (e2.X_op != O_register || reg2 > 127)
+    {
+      as_bad ("Second operand to .save not a valid register");
+      reg2 = 0;
+    }
+  switch (reg1)
+    {
+    case REG_AR + AR_BSP:
+      add_unwind_entry (output_bsp_when (), sep);
+      add_unwind_entry (output_bsp_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_BSPSTORE:
+      add_unwind_entry (output_bspstore_when (), sep);
+      add_unwind_entry (output_bspstore_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_RNAT:
+      add_unwind_entry (output_rnat_when (), sep);
+      add_unwind_entry (output_rnat_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_UNAT:
+      add_unwind_entry (output_unat_when (), sep);
+      add_unwind_entry (output_unat_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_FPSR:
+      add_unwind_entry (output_fpsr_when (), sep);
+      add_unwind_entry (output_fpsr_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_PFS:
+      add_unwind_entry (output_pfs_when (), sep);
+      if (! (unwind.prologue_mask & 4))
+       add_unwind_entry (output_pfs_gr (reg2), NOT_A_CHAR);
+      else if (reg2 != unwind.prologue_gr
+                      + (unsigned) popcount (unwind.prologue_mask & (-4 << 1)))
+       as_warn ("Second operand of .save contradicts .prologue");
+      break;
+    case REG_AR + AR_LC:
+      add_unwind_entry (output_lc_when (), sep);
+      add_unwind_entry (output_lc_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_BR:
+      add_unwind_entry (output_rp_when (), sep);
+      if (! (unwind.prologue_mask & 8))
+       add_unwind_entry (output_rp_gr (reg2), NOT_A_CHAR);
+      else if (reg2 != unwind.prologue_gr)
+       as_warn ("Second operand of .save contradicts .prologue");
+      break;
+    case REG_PR:
+      add_unwind_entry (output_preds_when (), sep);
+      if (! (unwind.prologue_mask & 1))
+       add_unwind_entry (output_preds_gr (reg2), NOT_A_CHAR);
+      else if (reg2 != unwind.prologue_gr
+                      + (unsigned) popcount (unwind.prologue_mask & (-1 << 1)))
+       as_warn ("Second operand of .save contradicts .prologue");
+      break;
+    case REG_PRIUNAT:
+      add_unwind_entry (output_priunat_when_gr (), sep);
+      add_unwind_entry (output_priunat_gr (reg2), NOT_A_CHAR);
+      break;
+    default:
+      as_bad ("First operand to .save not a valid register");
+      add_unwind_entry (NULL, sep);
+      break;
     }
-  else
-    as_bad ("First operand not a register");
 }
 
 static void
 dot_restore (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
+  expressionS e1;
   unsigned long ecount;        /* # of _additional_ regions to pop */
   int sep;
 
-  sep = parse_operand (&e1);
+  if (!in_body ("restore"))
+    return;
+
+  sep = parse_operand (&e1, ',');
   if (e1.X_op != O_register || e1.X_add_number != REG_GR + 12)
-    {
-      as_bad ("First operand to .restore must be stack pointer (sp)");
-      return;
-    }
+    as_bad ("First operand to .restore must be stack pointer (sp)");
 
   if (sep == ',')
     {
-      parse_operand (&e2);
+      expressionS e2;
+
+      sep = parse_operand (&e2, ',');
       if (e2.X_op != O_constant || e2.X_add_number < 0)
        {
          as_bad ("Second operand to .restore must be a constant >= 0");
-         return;
+         e2.X_add_number = 0;
        }
       ecount = e2.X_add_number;
     }
@@ -3232,10 +3623,10 @@ dot_restore (dummy)
     {
       as_bad ("Epilogue count of %lu exceeds number of nested prologues (%u)",
              ecount + 1, unwind.prologue_count);
-      return;
+      ecount = 0;
     }
 
-  add_unwind_entry (output_epilogue (ecount));
+  add_unwind_entry (output_epilogue (ecount), sep);
 
   if (ecount < unwind.prologue_count)
     unwind.prologue_count -= ecount + 1;
@@ -3244,52 +3635,27 @@ dot_restore (dummy)
 }
 
 static void
-dot_restorereg (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  unsigned int ab, reg;
-  expressionS e;
-
-  parse_operand (&e);
-
-  if (!convert_expr_to_ab_reg (&e, &ab, &reg))
-    {
-      as_bad ("First operand to .restorereg must be a preserved register");
-      return;
-    }
-  add_unwind_entry (output_spill_reg (ab, reg, 0, 0));
-}
-
-static void
-dot_restorereg_p (dummy)
-     int dummy ATTRIBUTE_UNUSED;
+dot_restorereg (pred)
+     int pred;
 {
   unsigned int qp, ab, reg;
-  expressionS e1, e2;
+  expressionS e;
   int sep;
+  const char * const po = pred ? "restorereg.p" : "restorereg";
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("No second operand to .restorereg.p");
-      return;
-    }
-
-  parse_operand (&e2);
+  if (!in_procedure (po))
+    return;
 
-  qp = e1.X_add_number - REG_P;
-  if (e1.X_op != O_register || qp > 63)
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
+  else
     {
-      as_bad ("First operand to .restorereg.p must be a predicate");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
-    {
-      as_bad ("Second operand to .restorereg.p must be a preserved register");
-      return;
-    }
-  add_unwind_entry (output_spill_reg_p (ab, reg, 0, 0, qp));
+  add_unwind_entry (output_spill_reg (ab, reg, 0, 0, qp), sep);
 }
 
 static char *special_linkonce_name[] =
@@ -3298,7 +3664,7 @@ static char *special_linkonce_name[] =
   };
 
 static void
-start_unwind_section (const segT text_seg, int sec_index, int linkonce_empty)
+start_unwind_section (const segT text_seg, int sec_index)
 {
   /*
     Use a slightly ugly scheme to derive the unwind section names from
@@ -3360,8 +3726,6 @@ start_unwind_section (const segT text_seg, int sec_index, int linkonce_empty)
       prefix = special_linkonce_name [sec_index - SPECIAL_SECTION_UNWIND];
       suffix += sizeof (".gnu.linkonce.t.") - 1;
     }
-  else if (linkonce_empty)
-    return;
 
   prefix_len = strlen (prefix);
   suffix_len = strlen (suffix);
@@ -3372,7 +3736,8 @@ start_unwind_section (const segT text_seg, int sec_index, int linkonce_empty)
   sec_name [sec_name_len] = '\0';
 
   /* Handle COMDAT group.  */
-  if (suffix == text_name && (text_seg->flags & SEC_LINK_ONCE) != 0)
+  if ((text_seg->flags & SEC_LINK_ONCE) != 0
+      && (elf_section_flags (text_seg) & SHF_GROUP) != 0)
     {
       char *section;
       size_t len, group_name_len;
@@ -3418,7 +3783,7 @@ generate_unwind_image (const segT text_seg)
 
   /* Mark the end of the unwind info, so that we can compute the size of the
      last unwind region.  */
-  add_unwind_entry (output_endp ());
+  add_unwind_entry (output_endp (), NOT_A_CHAR);
 
   /* Force out pending instructions, to make sure all unwind records have
      a valid slot_number field.  */
@@ -3436,9 +3801,11 @@ generate_unwind_image (const segT text_seg)
       pad = size % md.pointer_size;
       if (pad != 0)
        size += md.pointer_size - pad;
-      /* Add 8 for the header + a pointer for the personality
-        offset.  */
-      size += 8 + md.pointer_size;
+      /* Add 8 for the header.  */
+      size += 8;
+      /* Add a pointer for the personality offset.  */
+      if (unwind.personality_routine)
+       size += md.pointer_size;
     }
 
   /* If there are unwind records, switch sections, and output the info.  */
@@ -3447,7 +3814,7 @@ generate_unwind_image (const segT text_seg)
       expressionS exp;
       bfd_reloc_code_real_type reloc;
 
-      start_unwind_section (text_seg, SPECIAL_SECTION_UNWIND_INFO, 0);
+      start_unwind_section (text_seg, SPECIAL_SECTION_UNWIND_INFO);
 
       /* Make sure the section has 4 byte alignment for ILP32 and
         8 byte alignment for LP64.  */
@@ -3488,8 +3855,6 @@ generate_unwind_image (const segT text_seg)
          unwind.personality_routine = 0;
        }
     }
-  else
-    start_unwind_section (text_seg, SPECIAL_SECTION_UNWIND_INFO, 1);
 
   free_saved_prologue_counts ();
   unwind.list = unwind.tail = unwind.current_entry = NULL;
@@ -3499,6 +3864,8 @@ static void
 dot_handlerdata (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("handlerdata"))
+    return;
   unwind.force_unwind_entry = 1;
 
   /* Remember which segment we're in so we can switch back after .endp */
@@ -3516,6 +3883,8 @@ static void
 dot_unwentry (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("unwentry"))
+    return;
   unwind.force_unwind_entry = 1;
   demand_empty_rest_of_line ();
 }
@@ -3527,12 +3896,17 @@ dot_altrp (dummy)
   expressionS e;
   unsigned reg;
 
-  parse_operand (&e);
+  if (!in_prologue ("altrp"))
+    return;
+
+  parse_operand (&e, 0);
   reg = e.X_add_number - REG_BR;
-  if (e.X_op == O_register && reg < 8)
-    add_unwind_entry (output_rp_br (reg));
-  else
-    as_bad ("First operand not a valid branch register");
+  if (e.X_op != O_register || reg > 7)
+    {
+      as_bad ("First operand to .altrp not a valid branch register");
+      reg = 0;
+    }
+  add_unwind_entry (output_rp_br (reg), 0);
 }
 
 static void
@@ -3542,371 +3916,343 @@ dot_savemem (psprel)
   expressionS e1, e2;
   int sep;
   int reg1, val;
+  const char * const po = psprel ? "savepsp" : "savesp";
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    as_bad ("No second operand to .save%ssp", psprel ? "p" : "");
-  sep = parse_operand (&e2);
+  if (!in_prologue (po))
+    return;
+
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    sep = parse_operand (&e2, ',');
+  else
+    e2.X_op = O_absent;
 
   reg1 = e1.X_add_number;
   val = e2.X_add_number;
 
   /* Make sure its a valid ar.xxx reg, OR its br0, aka 'rp'.  */
-  if (e1.X_op == O_register)
+  if (e1.X_op != O_register)
     {
-      if (e2.X_op == O_constant)
-       {
-         switch (reg1)
-           {
-           case REG_AR + AR_BSP:
-             add_unwind_entry (output_bsp_when ());
-             add_unwind_entry ((psprel
-                                ? output_bsp_psprel
-                                : output_bsp_sprel) (val));
-             break;
-           case REG_AR + AR_BSPSTORE:
-             add_unwind_entry (output_bspstore_when ());
-             add_unwind_entry ((psprel
-                                ? output_bspstore_psprel
-                                : output_bspstore_sprel) (val));
-             break;
-           case REG_AR + AR_RNAT:
-             add_unwind_entry (output_rnat_when ());
-             add_unwind_entry ((psprel
-                                ? output_rnat_psprel
-                                : output_rnat_sprel) (val));
-             break;
-           case REG_AR + AR_UNAT:
-             add_unwind_entry (output_unat_when ());
-             add_unwind_entry ((psprel
-                                ? output_unat_psprel
-                                : output_unat_sprel) (val));
-             break;
-           case REG_AR + AR_FPSR:
-             add_unwind_entry (output_fpsr_when ());
-             add_unwind_entry ((psprel
-                                ? output_fpsr_psprel
-                                : output_fpsr_sprel) (val));
-             break;
-           case REG_AR + AR_PFS:
-             add_unwind_entry (output_pfs_when ());
-             add_unwind_entry ((psprel
-                                ? output_pfs_psprel
-                                : output_pfs_sprel) (val));
-             break;
-           case REG_AR + AR_LC:
-             add_unwind_entry (output_lc_when ());
-             add_unwind_entry ((psprel
-                                ? output_lc_psprel
-                                : output_lc_sprel) (val));
-             break;
-           case REG_BR:
-             add_unwind_entry (output_rp_when ());
-             add_unwind_entry ((psprel
-                                ? output_rp_psprel
-                                : output_rp_sprel) (val));
-             break;
-           case REG_PR:
-             add_unwind_entry (output_preds_when ());
-             add_unwind_entry ((psprel
-                                ? output_preds_psprel
-                                : output_preds_sprel) (val));
-             break;
-           case REG_PRIUNAT:
-             add_unwind_entry (output_priunat_when_mem ());
-             add_unwind_entry ((psprel
-                                ? output_priunat_psprel
-                                : output_priunat_sprel) (val));
-             break;
-           default:
-             as_bad ("First operand not a valid register");
-           }
-       }
-      else
-       as_bad (" Second operand not a valid constant");
+      as_bad ("First operand to .%s not a register", po);
+      reg1 = REG_PR; /* Anything valid is good here.  */
     }
-  else
-    as_bad ("First operand not a register");
-}
-
-static void
-dot_saveg (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  expressionS e1, e2;
-  int sep;
-  sep = parse_operand (&e1);
-  if (sep == ',')
-    parse_operand (&e2);
-
-  if (e1.X_op != O_constant)
-    as_bad ("First operand to .save.g must be a constant.");
-  else
+  if (e2.X_op != O_constant)
     {
-      int grmask = e1.X_add_number;
-      if (sep != ',')
-       add_unwind_entry (output_gr_mem (grmask));
-      else
-       {
-         int reg = e2.X_add_number - REG_GR;
-         if (e2.X_op == O_register && reg >= 0 && reg < 128)
-           add_unwind_entry (output_gr_gr (grmask, reg));
-         else
-           as_bad ("Second operand is an invalid register.");
-       }
+      as_bad ("Second operand to .%s not a constant", po);
+      val = 0;
+    }
+
+  switch (reg1)
+    {
+    case REG_AR + AR_BSP:
+      add_unwind_entry (output_bsp_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_bsp_psprel
+                        : output_bsp_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_BSPSTORE:
+      add_unwind_entry (output_bspstore_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_bspstore_psprel
+                        : output_bspstore_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_RNAT:
+      add_unwind_entry (output_rnat_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_rnat_psprel
+                        : output_rnat_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_UNAT:
+      add_unwind_entry (output_unat_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_unat_psprel
+                        : output_unat_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_FPSR:
+      add_unwind_entry (output_fpsr_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_fpsr_psprel
+                        : output_fpsr_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_PFS:
+      add_unwind_entry (output_pfs_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_pfs_psprel
+                        : output_pfs_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_LC:
+      add_unwind_entry (output_lc_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_lc_psprel
+                        : output_lc_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_BR:
+      add_unwind_entry (output_rp_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_rp_psprel
+                        : output_rp_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_PR:
+      add_unwind_entry (output_preds_when (), sep);
+      add_unwind_entry ((psprel
+                        ? output_preds_psprel
+                        : output_preds_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_PRIUNAT:
+      add_unwind_entry (output_priunat_when_mem (), sep);
+      add_unwind_entry ((psprel
+                        ? output_priunat_psprel
+                        : output_priunat_sprel) (val), NOT_A_CHAR);
+      break;
+    default:
+      as_bad ("First operand to .%s not a valid register", po);
+      add_unwind_entry (NULL, sep);
+      break;
     }
 }
 
 static void
-dot_savef (dummy)
+dot_saveg (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1;
+  expressionS e;
+  unsigned grmask;
   int sep;
-  sep = parse_operand (&e1);
 
-  if (e1.X_op != O_constant)
-    as_bad ("Operand to .save.f must be a constant.");
-  else
-    add_unwind_entry (output_fr_mem (e1.X_add_number));
-}
+  if (!in_prologue ("save.g"))
+    return;
 
-static void
-dot_saveb (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  expressionS e1, e2;
-  unsigned int reg;
-  unsigned char sep;
-  int brmask;
+  sep = parse_operand (&e, ',');
 
-  sep = parse_operand (&e1);
-  if (e1.X_op != O_constant)
+  grmask = e.X_add_number;
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0xf)
     {
-      as_bad ("First operand to .save.b must be a constant.");
-      return;
+      as_bad ("First operand to .save.g must be a positive 4-bit constant");
+      grmask = 0;
     }
-  brmask = e1.X_add_number;
 
   if (sep == ',')
     {
-      sep = parse_operand (&e2);
-      reg = e2.X_add_number - REG_GR;
-      if (e2.X_op != O_register || reg > 127)
+      unsigned reg;
+      int n = popcount (grmask);
+
+      parse_operand (&e, 0);
+      reg = e.X_add_number - REG_GR;
+      if (e.X_op != O_register || reg > 127)
        {
-         as_bad ("Second operand to .save.b must be a general register.");
-         return;
+         as_bad ("Second operand to .save.g must be a general register");
+         reg = 0;
+       }
+      else if (reg > 128U - n)
+       {
+         as_bad ("Second operand to .save.g must be the first of %d general registers", n);
+         reg = 0;
        }
-      add_unwind_entry (output_br_gr (brmask, e2.X_add_number));
+      add_unwind_entry (output_gr_gr (grmask, reg), 0);
     }
   else
-    add_unwind_entry (output_br_mem (brmask));
-
-  if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    demand_empty_rest_of_line ();
+    add_unwind_entry (output_gr_mem (grmask), 0);
 }
 
 static void
-dot_savegf (dummy)
+dot_savef (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
-  int sep;
-  sep = parse_operand (&e1);
-  if (sep == ',')
-    parse_operand (&e2);
+  expressionS e;
 
-  if (e1.X_op != O_constant || sep != ',' || e2.X_op != O_constant)
-    as_bad ("Both operands of .save.gf must be constants.");
-  else
+  if (!in_prologue ("save.f"))
+    return;
+
+  parse_operand (&e, 0);
+
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0xfffff)
     {
-      int grmask = e1.X_add_number;
-      int frmask = e2.X_add_number;
-      add_unwind_entry (output_frgr_mem (grmask, frmask));
+      as_bad ("Operand to .save.f must be a positive 20-bit constant");
+      e.X_add_number = 0;
     }
+  add_unwind_entry (output_fr_mem (e.X_add_number), 0);
 }
 
 static void
-dot_spill (dummy)
+dot_saveb (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
-  unsigned char sep;
-
-  sep = parse_operand (&e);
-  if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    demand_empty_rest_of_line ();
+  unsigned brmask;
+  int sep;
 
-  if (e.X_op != O_constant)
-    as_bad ("Operand to .spill must be a constant");
-  else
-    add_unwind_entry (output_spill_base (e.X_add_number));
-}
+  if (!in_prologue ("save.b"))
+    return;
 
-static void
-dot_spillreg (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  int sep, ab, xy, reg, treg;
-  expressionS e1, e2;
+  sep = parse_operand (&e, ',');
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
+  brmask = e.X_add_number;
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0x1f)
     {
-      as_bad ("No second operand to .spillreg");
-      return;
+      as_bad ("First operand to .save.b must be a positive 5-bit constant");
+      brmask = 0;
     }
 
-  parse_operand (&e2);
-
-  if (!convert_expr_to_ab_reg (&e1, &ab, &reg))
+  if (sep == ',')
     {
-      as_bad ("First operand to .spillreg must be a preserved register");
-      return;
-    }
+      unsigned reg;
+      int n = popcount (brmask);
 
-  if (!convert_expr_to_xy_reg (&e2, &xy, &treg))
-    {
-      as_bad ("Second operand to .spillreg must be a register");
-      return;
+      parse_operand (&e, 0);
+      reg = e.X_add_number - REG_GR;
+      if (e.X_op != O_register || reg > 127)
+       {
+         as_bad ("Second operand to .save.b must be a general register");
+         reg = 0;
+       }
+      else if (reg > 128U - n)
+       {
+         as_bad ("Second operand to .save.b must be the first of %d general registers", n);
+         reg = 0;
+       }
+      add_unwind_entry (output_br_gr (brmask, reg), 0);
     }
-
-  add_unwind_entry (output_spill_reg (ab, reg, treg, xy));
+  else
+    add_unwind_entry (output_br_mem (brmask), 0);
 }
 
 static void
-dot_spillmem (psprel)
-     int psprel;
+dot_savegf (dummy)
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
-  int sep, ab, reg;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand missing");
-      return;
-    }
+  if (!in_prologue ("save.gf"))
+    return;
 
-  parse_operand (&e2);
+  if (parse_operand (&e1, ',') == ',')
+    parse_operand (&e2, 0);
+  else
+    e2.X_op = O_absent;
 
-  if (!convert_expr_to_ab_reg (&e1, &ab, &reg))
+  if (e1.X_op != O_constant
+      || e1.X_add_number < 0
+      || e1.X_add_number > 0xf)
     {
-      as_bad ("First operand to .spill%s must be a preserved register",
-             psprel ? "psp" : "sp");
-      return;
+      as_bad ("First operand to .save.gf must be a non-negative 4-bit constant");
+      e1.X_op = O_absent;
+      e1.X_add_number = 0;
     }
-
-  if (e2.X_op != O_constant)
+  if (e2.X_op != O_constant
+      || e2.X_add_number < 0
+      || e2.X_add_number > 0xfffff)
     {
-      as_bad ("Second operand to .spill%s must be a constant",
-             psprel ? "psp" : "sp");
-      return;
+      as_bad ("Second operand to .save.gf must be a non-negative 20-bit constant");
+      e2.X_op = O_absent;
+      e2.X_add_number = 0;
     }
+  if (e1.X_op == O_constant
+      && e2.X_op == O_constant
+      && e1.X_add_number == 0
+      && e2.X_add_number == 0)
+    as_bad ("Operands to .save.gf may not be both zero");
 
-  if (psprel)
-    add_unwind_entry (output_spill_psprel (ab, reg, e2.X_add_number));
-  else
-    add_unwind_entry (output_spill_sprel (ab, reg, e2.X_add_number));
+  add_unwind_entry (output_frgr_mem (e1.X_add_number, e2.X_add_number), 0);
 }
 
 static void
-dot_spillreg_p (dummy)
+dot_spill (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  int sep, ab, xy, reg, treg;
-  expressionS e1, e2, e3;
-  unsigned int qp;
+  expressionS e;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("No second and third operand to .spillreg.p");
-      return;
-    }
+  if (!in_prologue ("spill"))
+    return;
+
+  parse_operand (&e, 0);
 
-  sep = parse_operand (&e2);
-  if (sep != ',')
+  if (e.X_op != O_constant)
     {
-      as_bad ("No third operand to .spillreg.p");
-      return;
+      as_bad ("Operand to .spill must be a constant");
+      e.X_add_number = 0;
     }
+  add_unwind_entry (output_spill_base (e.X_add_number), 0);
+}
 
-  parse_operand (&e3);
-
-  qp = e1.X_add_number - REG_P;
+static void
+dot_spillreg (pred)
+     int pred;
+{
+  int sep;
+  unsigned int qp, ab, xy, reg, treg;
+  expressionS e;
+  const char * const po = pred ? "spillreg.p" : "spillreg";
 
-  if (e1.X_op != O_register || qp > 63)
-    {
-      as_bad ("First operand to .spillreg.p must be a predicate");
-      return;
-    }
+  if (!in_procedure (po))
+    return;
 
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
+  else
     {
-      as_bad ("Second operand to .spillreg.p must be a preserved register");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  if (!convert_expr_to_xy_reg (&e3, &xy, &treg))
-    {
-      as_bad ("Third operand to .spillreg.p must be a register");
-      return;
-    }
+  if (sep == ',')
+    sep = parse_operand (&e, ',');
+  else
+    e.X_op = O_absent;
+  convert_expr_to_xy_reg (&e, &xy, &treg, po, 2 + pred);
 
-  add_unwind_entry (output_spill_reg_p (ab, reg, treg, xy, qp));
+  add_unwind_entry (output_spill_reg (ab, reg, treg, xy, qp), sep);
 }
 
 static void
-dot_spillmem_p (psprel)
+dot_spillmem (psprel)
      int psprel;
 {
-  expressionS e1, e2, e3;
-  int sep, ab, reg;
-  unsigned int qp;
-
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand missing");
-      return;
-    }
+  expressionS e;
+  int pred = (psprel < 0), sep;
+  unsigned int qp, ab, reg;
+  const char * po;
 
-  parse_operand (&e2);
-  if (sep != ',')
+  if (pred)
     {
-      as_bad ("Second operand missing");
-      return;
+      psprel = ~psprel;
+      po = psprel ? "spillpsp.p" : "spillsp.p";
     }
+  else
+    po = psprel ? "spillpsp" : "spillsp";
 
-  parse_operand (&e3);
+  if (!in_procedure (po))
+    return;
 
-  qp = e1.X_add_number - REG_P;
-  if (e1.X_op != O_register || qp > 63)
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
+  else
     {
-      as_bad ("First operand to .spill%s_p must be a predicate",
-             psprel ? "psp" : "sp");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
-    {
-      as_bad ("Second operand to .spill%s_p must be a preserved register",
-             psprel ? "psp" : "sp");
-      return;
-    }
-
-  if (e3.X_op != O_constant)
+  if (sep == ',')
+    sep = parse_operand (&e, ',');
+  else
+    e.X_op = O_absent;
+  if (e.X_op != O_constant)
     {
-      as_bad ("Third operand to .spill%s_p must be a constant",
-             psprel ? "psp" : "sp");
-      return;
+      as_bad ("Operand %d to .%s must be a constant", 2 + pred, po);
+      e.X_add_number = 0;
     }
 
   if (psprel)
-    add_unwind_entry (output_spill_psprel_p (ab, reg, e3.X_add_number, qp));
+    add_unwind_entry (output_spill_psprel (ab, reg, e.X_add_number, qp), sep);
   else
-    add_unwind_entry (output_spill_sprel_p (ab, reg, e3.X_add_number, qp));
+    add_unwind_entry (output_spill_sprel (ab, reg, e.X_add_number, qp), sep);
 }
 
 static unsigned int
@@ -3970,14 +4316,18 @@ dot_label_state (dummy)
 {
   expressionS e;
 
-  parse_operand (&e);
-  if (e.X_op != O_constant)
+  if (!in_body ("label_state"))
+    return;
+
+  parse_operand (&e, 0);
+  if (e.X_op == O_constant)
+    save_prologue_count (e.X_add_number, unwind.prologue_count);
+  else
     {
       as_bad ("Operand to .label_state must be a constant");
-      return;
+      e.X_add_number = 0;
     }
-  add_unwind_entry (output_label_state (e.X_add_number));
-  save_prologue_count (e.X_add_number, unwind.prologue_count);
+  add_unwind_entry (output_label_state (e.X_add_number), 0);
 }
 
 static void
@@ -3986,14 +4336,18 @@ dot_copy_state (dummy)
 {
   expressionS e;
 
-  parse_operand (&e);
-  if (e.X_op != O_constant)
+  if (!in_body ("copy_state"))
+    return;
+
+  parse_operand (&e, 0);
+  if (e.X_op == O_constant)
+    unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
+  else
     {
       as_bad ("Operand to .copy_state must be a constant");
-      return;
+      e.X_add_number = 0;
     }
-  add_unwind_entry (output_copy_state (e.X_add_number));
-  unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
+  add_unwind_entry (output_copy_state (e.X_add_number), 0);
 }
 
 static void
@@ -4003,29 +4357,28 @@ dot_unwabi (dummy)
   expressionS e1, e2;
   unsigned char sep;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand to .unwabi missing");
-      return;
-    }
-  sep = parse_operand (&e2);
-  if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    demand_empty_rest_of_line ();
+  if (!in_prologue ("unwabi"))
+    return;
+
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    parse_operand (&e2, 0);
+  else
+    e2.X_op = O_absent;
 
   if (e1.X_op != O_constant)
     {
       as_bad ("First operand to .unwabi must be a constant");
-      return;
+      e1.X_add_number = 0;
     }
 
   if (e2.X_op != O_constant)
     {
       as_bad ("Second operand to .unwabi must be a constant");
-      return;
+      e2.X_add_number = 0;
     }
 
-  add_unwind_entry (output_unwabi (e1.X_add_number, e2.X_add_number));
+  add_unwind_entry (output_unwabi (e1.X_add_number, e2.X_add_number), 0);
 }
 
 static void
@@ -4033,6 +4386,8 @@ dot_personality (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   char *name, *p, c;
+  if (!in_procedure ("personality"))
+    return;
   SKIP_WHITESPACE ();
   name = input_line_pointer;
   c = get_symbol_end ();
@@ -4050,8 +4405,22 @@ dot_proc (dummy)
 {
   char *name, *p, c;
   symbolS *sym;
+  proc_pending *pending, *last_pending;
+
+  if (unwind.proc_pending.sym)
+    {
+      (md.unwind_check == unwind_check_warning
+       ? as_warn
+       : as_bad) ("Missing .endp after previous .proc");
+      while (unwind.proc_pending.next)
+       {
+         pending = unwind.proc_pending.next;
+         unwind.proc_pending.next = pending->next;
+         free (pending);
+       }
+    }
+  last_pending = NULL;
 
-  unwind.proc_start = expr_build_dot ();
   /* Parse names of main and alternate entry points and mark them as
      function symbols:  */
   while (1)
@@ -4060,22 +4429,45 @@ dot_proc (dummy)
       name = input_line_pointer;
       c = get_symbol_end ();
       p = input_line_pointer;
-      sym = symbol_find_or_make (name);
-      if (unwind.proc_start == 0)
+      if (!*name)
+       as_bad ("Empty argument of .proc");
+      else
        {
-         unwind.proc_start = sym;
+         sym = symbol_find_or_make (name);
+         if (S_IS_DEFINED (sym))
+           as_bad ("`%s' was already defined", name);
+         else if (!last_pending)
+           {
+             unwind.proc_pending.sym = sym;
+             last_pending = &unwind.proc_pending;
+           }
+         else
+           {
+             pending = xmalloc (sizeof (*pending));
+             pending->sym = sym;
+             last_pending = last_pending->next = pending;
+           }
+         symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
        }
-      symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
       *p = c;
       SKIP_WHITESPACE ();
       if (*input_line_pointer != ',')
        break;
       ++input_line_pointer;
     }
+  if (!last_pending)
+    {
+      unwind.proc_pending.sym = expr_build_dot ();
+      last_pending = &unwind.proc_pending;
+    }
+  last_pending->next = NULL;
   demand_empty_rest_of_line ();
   ia64_do_align (16);
 
+  unwind.prologue = 0;
   unwind.prologue_count = 0;
+  unwind.body = 0;
+  unwind.insn = 0;
   unwind.list = unwind.tail = unwind.current_entry = NULL;
   unwind.personality_routine = 0;
 }
@@ -4084,52 +4476,86 @@ static void
 dot_body (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("body"))
+    return;
+  if (!unwind.prologue && !unwind.body && unwind.insn)
+    as_warn ("Initial .body should precede any instructions");
+  check_pending_save ();
+
   unwind.prologue = 0;
   unwind.prologue_mask = 0;
+  unwind.body = 1;
 
-  add_unwind_entry (output_body ());
-  demand_empty_rest_of_line ();
+  add_unwind_entry (output_body (), 0);
 }
 
 static void
 dot_prologue (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  unsigned char sep;
-  int mask = 0, grsave = 0;
+  unsigned mask = 0, grsave = 0;
 
-  if (!is_it_end_of_statement ())
+  if (!in_procedure ("prologue"))
+    return;
+  if (unwind.prologue)
     {
-      expressionS e1, e2;
-      sep = parse_operand (&e1);
-      if (sep != ',')
-       as_bad ("No second operand to .prologue");
-      sep = parse_operand (&e2);
-      if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-       demand_empty_rest_of_line ();
+      as_bad (".prologue within prologue");
+      ignore_rest_of_line ();
+      return;
+    }
+  if (!unwind.body && unwind.insn)
+    as_warn ("Initial .prologue should precede any instructions");
 
-      if (e1.X_op == O_constant)
-       {
-         mask = e1.X_add_number;
+  if (!is_it_end_of_statement ())
+    {
+      expressionS e;
+      int n, sep = parse_operand (&e, ',');
 
-         if (e2.X_op == O_constant)
-           grsave = e2.X_add_number;
-         else if (e2.X_op == O_register
-                  && (grsave = e2.X_add_number - REG_GR) < 128)
-           ;
-         else
-           as_bad ("Second operand not a constant or general register");
+      if (e.X_op != O_constant
+         || e.X_add_number < 0
+         || e.X_add_number > 0xf)
+       as_bad ("First operand to .prologue must be a positive 4-bit constant");
+      else if (e.X_add_number == 0)
+       as_warn ("Pointless use of zero first operand to .prologue");
+      else
+       mask = e.X_add_number;
+       n = popcount (mask);
 
-         add_unwind_entry (output_prologue_gr (mask, grsave));
-       }
+      if (sep == ',')
+       parse_operand (&e, 0);
       else
-       as_bad ("First operand not a constant");
+       e.X_op = O_absent;
+      if (e.X_op == O_constant
+         && e.X_add_number >= 0
+         && e.X_add_number < 128)
+       {
+         if (md.unwind_check == unwind_check_error)
+           as_warn ("Using a constant as second operand to .prologue is deprecated");
+         grsave = e.X_add_number;
+       }
+      else if (e.X_op != O_register
+              || (grsave = e.X_add_number - REG_GR) > 127)
+       {
+         as_bad ("Second operand to .prologue must be a general register");
+         grsave = 0;
+       }
+      else if (grsave > 128U - n)
+       {
+         as_bad ("Second operand to .prologue must be the first of %d general registers", n);
+         grsave = 0;
+       }
+
     }
+
+  if (mask)
+    add_unwind_entry (output_prologue_gr (mask, grsave), 0);
   else
-    add_unwind_entry (output_prologue ());
+    add_unwind_entry (output_prologue (), 0);
 
   unwind.prologue = 1;
   unwind.prologue_mask = mask;
+  unwind.prologue_gr = grsave;
+  unwind.body = 0;
   ++unwind.prologue_count;
 }
 
@@ -4138,13 +4564,17 @@ dot_endp (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
-  unsigned char *ptr;
   int bytes_per_address;
   long where;
   segT saved_seg;
   subsegT saved_subseg;
-  char *name, *p, c;
-  symbolS *sym;
+  proc_pending *pending;
+  int unwind_check = md.unwind_check;
+
+  md.unwind_check = unwind_check_error;
+  if (!in_procedure ("endp"))
+    return;
+  md.unwind_check = unwind_check;
 
   if (unwind.saved_text_seg)
     {
@@ -4166,10 +4596,12 @@ dot_endp (dummy)
 
   if (unwind.info || unwind.force_unwind_entry)
     {
+      symbolS *proc_end;
+
       subseg_set (md.last_text_seg, 0);
-      unwind.proc_end = expr_build_dot ();
+      proc_end = expr_build_dot ();
 
-      start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND, 0);
+      start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND);
 
       /* Make sure that section has 4 byte alignment for ILP32 and
          8 byte alignment for LP64.  */
@@ -4177,7 +4609,7 @@ dot_endp (dummy)
 
       /* Need space for 3 pointers for procedure start, procedure end,
         and unwind info.  */
-      ptr = frag_more (3 * md.pointer_size);
+      memset (frag_more (3 * md.pointer_size), 0, 3 * md.pointer_size);
       where = frag_now_fix () - (3 * md.pointer_size);
       bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8;
 
@@ -4185,13 +4617,19 @@ dot_endp (dummy)
       e.X_op = O_pseudo_fixup;
       e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
       e.X_add_number = 0;
-      e.X_add_symbol = unwind.proc_start;
+      if (!S_IS_LOCAL (unwind.proc_pending.sym)
+         && S_IS_DEFINED (unwind.proc_pending.sym))
+       e.X_add_symbol = symbol_temp_new (S_GET_SEGMENT (unwind.proc_pending.sym),
+                                         S_GET_VALUE (unwind.proc_pending.sym),
+                                         symbol_get_frag (unwind.proc_pending.sym));
+      else
+       e.X_add_symbol = unwind.proc_pending.sym;
       ia64_cons_fix_new (frag_now, where, bytes_per_address, &e);
 
       e.X_op = O_pseudo_fixup;
       e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
       e.X_add_number = 0;
-      e.X_add_symbol = unwind.proc_end;
+      e.X_add_symbol = proc_end;
       ia64_cons_fix_new (frag_now, where + bytes_per_address,
                         bytes_per_address, &e);
 
@@ -4204,51 +4642,71 @@ dot_endp (dummy)
          ia64_cons_fix_new (frag_now, where + (bytes_per_address * 2),
                             bytes_per_address, &e);
        }
-      else
-       md_number_to_chars (ptr + (bytes_per_address * 2), 0,
-                           bytes_per_address);
-
     }
-  else
-    start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND, 1);
-
   subseg_set (saved_seg, saved_subseg);
 
-  /* Parse names of main and alternate entry points and set symbol sizes.  */
+  /* Set symbol sizes.  */
+  pending = &unwind.proc_pending;
+  if (S_GET_NAME (pending->sym))
+    {
+      do
+       {
+         symbolS *sym = pending->sym;
+
+         if (!S_IS_DEFINED (sym))
+           as_bad ("`%s' was not defined within procedure", S_GET_NAME (sym));
+         else if (S_GET_SIZE (sym) == 0
+                  && symbol_get_obj (sym)->size == NULL)
+           {
+             fragS *frag = symbol_get_frag (sym);
+
+             if (frag)
+               {
+                 if (frag == frag_now && SEG_NORMAL (now_seg))
+                   S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym));
+                 else
+                   {
+                     symbol_get_obj (sym)->size =
+                       (expressionS *) xmalloc (sizeof (expressionS));
+                     symbol_get_obj (sym)->size->X_op = O_subtract;
+                     symbol_get_obj (sym)->size->X_add_symbol
+                       = symbol_new (FAKE_LABEL_NAME, now_seg,
+                                     frag_now_fix (), frag_now);
+                     symbol_get_obj (sym)->size->X_op_symbol = sym;
+                     symbol_get_obj (sym)->size->X_add_number = 0;
+                   }
+               }
+           }
+       } while ((pending = pending->next) != NULL);
+    }
+
+  /* Parse names of main and alternate entry points.  */
   while (1)
     {
+      char *name, *p, c;
+
       SKIP_WHITESPACE ();
       name = input_line_pointer;
       c = get_symbol_end ();
       p = input_line_pointer;
-      sym = symbol_find (name);
-      if (sym && unwind.proc_start
-         && (symbol_get_bfdsym (sym)->flags & BSF_FUNCTION)
-         && S_GET_SIZE (sym) == 0 && symbol_get_obj (sym)->size == NULL)
-       {
-         fragS *fr = symbol_get_frag (unwind.proc_start);
-         fragS *frag = symbol_get_frag (sym);
-
-         /* Check whether the function label is at or beyond last
-            .proc directive.  */
-         while (fr && fr != frag)
-           fr = fr->fr_next;
-         if (fr)
-           {
-             if (frag == frag_now && SEG_NORMAL (now_seg))
-               S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym));
-             else
+      if (!*name)
+       (md.unwind_check == unwind_check_warning
+        ? as_warn
+        : as_bad) ("Empty argument of .endp");
+      else
+       {
+         symbolS *sym = symbol_find (name);
+
+         for (pending = &unwind.proc_pending; pending; pending = pending->next)
+           {
+             if (sym == pending->sym)
                {
-                 symbol_get_obj (sym)->size =
-                   (expressionS *) xmalloc (sizeof (expressionS));
-                 symbol_get_obj (sym)->size->X_op = O_subtract;
-                 symbol_get_obj (sym)->size->X_add_symbol
-                   = symbol_new (FAKE_LABEL_NAME, now_seg,
-                                 frag_now_fix (), frag_now);
-                 symbol_get_obj (sym)->size->X_op_symbol = sym;
-                 symbol_get_obj (sym)->size->X_add_number = 0;
+                 pending->sym = NULL;
+                 break;
                }
            }
+         if (!sym || !pending)
+           as_warn ("`%s' was not specified with previous .proc", name);
        }
       *p = c;
       SKIP_WHITESPACE ();
@@ -4257,7 +4715,21 @@ dot_endp (dummy)
       ++input_line_pointer;
     }
   demand_empty_rest_of_line ();
-  unwind.proc_start = unwind.proc_end = unwind.info = 0;
+
+  /* Deliberately only checking for the main entry point here; the
+     language spec even says all arguments to .endp are ignored.  */
+  if (unwind.proc_pending.sym
+      && S_GET_NAME (unwind.proc_pending.sym)
+      && strcmp (S_GET_NAME (unwind.proc_pending.sym), FAKE_LABEL_NAME))
+    as_warn ("`%s' should be an operand to this .endp",
+            S_GET_NAME (unwind.proc_pending.sym));
+  while (unwind.proc_pending.next)
+    {
+      pending = unwind.proc_pending.next;
+      unwind.proc_pending.next = pending->next;
+      free (pending);
+    }
+  unwind.proc_pending.sym = unwind.info = NULL;
 }
 
 static void
@@ -4318,6 +4790,7 @@ dot_rot (type)
   for (dr = md.dynreg[type]; dr && dr->num_regs; dr = dr->next)
     {
       hash_delete (md.dynreg_hash, dr->name);
+      /* FIXME: Free dr->name.  */
       dr->num_regs = 0;
     }
 
@@ -4326,8 +4799,8 @@ dot_rot (type)
     {
       start = input_line_pointer;
       ch = get_symbol_end ();
+      len = strlen (ia64_canonicalize_symbol_name (start));
       *input_line_pointer = ch;
-      len = (input_line_pointer - start);
 
       SKIP_WHITESPACE ();
       if (*input_line_pointer != '[')
@@ -4376,16 +4849,16 @@ dot_rot (type)
          break;
        }
 
-      name = obstack_alloc (&notes, len + 1);
-      memcpy (name, start, len);
-      name[len] = '\0';
-
       if (!*drpp)
        {
          *drpp = obstack_alloc (&notes, sizeof (*dr));
          memset (*drpp, 0, sizeof (*dr));
        }
 
+      name = obstack_alloc (&notes, len + 1);
+      memcpy (name, start, len);
+      name[len] = '\0';
+
       dr = *drpp;
       dr->name = name;
       dr->num_regs = num_regs;
@@ -4396,6 +4869,7 @@ dot_rot (type)
       if (hash_insert (md.dynreg_hash, name, dr))
        {
          as_bad ("Attempt to redefine register set `%s'", name);
+         obstack_free (&notes, name);
          goto err;
        }
 
@@ -4483,49 +4957,75 @@ dot_ln (dummy)
   demand_empty_rest_of_line ();
 }
 
-static char *
-parse_section_name ()
+static void
+cross_section (ref, cons, ua)
+     int ref;
+     void (*cons) PARAMS((int));
+     int ua;
 {
-  char *name;
-  int len;
+  char *start, *end;
+  int saved_auto_align;
+  unsigned int section_count;
 
   SKIP_WHITESPACE ();
-  if (*input_line_pointer != '"')
+  start = input_line_pointer;
+  if (*start == '"')
     {
-      as_bad ("Missing section name");
-      ignore_rest_of_line ();
-      return 0;
+      int len;
+      char *name;
+
+      name = demand_copy_C_string (&len);
+      obstack_free(&notes, name);
+      if (!name)
+       {
+         ignore_rest_of_line ();
+         return;
+       }
     }
-  name = demand_copy_C_string (&len);
-  if (!name)
+  else
     {
-      ignore_rest_of_line ();
-      return 0;
+      char c = get_symbol_end ();
+
+      if (input_line_pointer == start)
+       {
+         as_bad ("Missing section name");
+         ignore_rest_of_line ();
+         return;
+       }
+      *input_line_pointer = c;
     }
+  end = input_line_pointer;
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
     {
       as_bad ("Comma expected after section name");
       ignore_rest_of_line ();
-      return 0;
+      return;
     }
-  ++input_line_pointer;                /* skip comma */
-  return name;
+  *end = '\0';
+  end = input_line_pointer + 1;                /* skip comma */
+  input_line_pointer = start;
+  md.keep_pending_output = 1;
+  section_count = bfd_count_sections(stdoutput);
+  obj_elf_section (0);
+  if (section_count != bfd_count_sections(stdoutput))
+    as_warn ("Creating sections with .xdataN/.xrealN/.xstringZ is deprecated.");
+  input_line_pointer = end;
+  saved_auto_align = md.auto_align;
+  if (ua)
+    md.auto_align = 0;
+  (*cons) (ref);
+  if (ua)
+    md.auto_align = saved_auto_align;
+  obj_elf_previous (0);
+  md.keep_pending_output = 0;
 }
 
 static void
 dot_xdata (size)
      int size;
 {
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  cons (size);
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (size, cons, 0);
 }
 
 /* Why doesn't float_cons() call md_cons_align() the way cons() does?  */
@@ -4571,66 +5071,28 @@ static void
 dot_xfloat_cons (kind)
      int kind;
 {
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  stmt_float_cons (kind);
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (kind, stmt_float_cons, 0);
 }
 
 static void
 dot_xstringer (zero)
      int zero;
 {
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  stringer (zero);
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (zero, stringer, 0);
 }
 
 static void
 dot_xdata_ua (size)
      int size;
 {
-  int saved_auto_align = md.auto_align;
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  md.auto_align = 0;
-  cons (size);
-  md.auto_align = saved_auto_align;
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (size, cons, 1);
 }
 
 static void
 dot_xfloat_cons_ua (kind)
      int kind;
 {
-  int saved_auto_align = md.auto_align;
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  md.auto_align = 0;
-  stmt_float_cons (kind);
-  md.auto_align = saved_auto_align;
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (kind, float_cons, 1);
 }
 
 /* .reg.val <regname>,value */
@@ -4656,7 +5118,7 @@ dot_reg_val (dummy)
     {
       valueT value = get_absolute_expression ();
       int regno = reg.X_add_number;
-      if (regno < REG_GR || regno > REG_GR + 128)
+      if (regno <= REG_GR || regno > REG_GR + 127)
        as_warn (_("Register value annotation ignored"));
       else
        {
@@ -4747,9 +5209,9 @@ print_prmask (mask)
 }
 
 /*
-  .pred.rel.clear [p1 [,p2 [,...]]]     (also .pred.rel "clear")
-  .pred.rel.imply p1, p2                (also .pred.rel "imply")
-  .pred.rel.mutex p1, p2 [,...]         (also .pred.rel "mutex")
+  .pred.rel.clear [p1 [,p2 [,...]]]     (also .pred.rel "clear" or @clear)
+  .pred.rel.imply p1, p2                (also .pred.rel "imply" or @imply)
+  .pred.rel.mutex p1, p2 [,...]         (also .pred.rel "mutex" or @mutex)
   .pred.safe_across_calls p1 [, p2 [,...]]
  */
 
@@ -4763,28 +5225,43 @@ dot_pred_rel (type)
 
   if (type == 0)
     {
-      if (*input_line_pointer != '"')
-       {
-         as_bad (_("Missing predicate relation type"));
-         ignore_rest_of_line ();
-         return;
-       }
-      else
+      if (*input_line_pointer == '"')
        {
          int len;
          char *form = demand_copy_C_string (&len);
+
          if (strcmp (form, "mutex") == 0)
            type = 'm';
          else if (strcmp (form, "clear") == 0)
            type = 'c';
          else if (strcmp (form, "imply") == 0)
            type = 'i';
-         else
-           {
-             as_bad (_("Unrecognized predicate relation type"));
-             ignore_rest_of_line ();
-             return;
-           }
+         obstack_free (&notes, form);
+       }
+      else if (*input_line_pointer == '@')
+       {
+         char *form = ++input_line_pointer;
+         char c = get_symbol_end();
+
+         if (strcmp (form, "mutex") == 0)
+           type = 'm';
+         else if (strcmp (form, "clear") == 0)
+           type = 'c';
+         else if (strcmp (form, "imply") == 0)
+           type = 'i';
+         *input_line_pointer = c;
+       }
+      else
+       {
+         as_bad (_("Missing predicate relation type"));
+         ignore_rest_of_line ();
+         return;
+       }
+      if (type == 0)
+       {
+         as_bad (_("Unrecognized predicate relation type"));
+         ignore_rest_of_line ();
+         return;
        }
       if (*input_line_pointer == ',')
        ++input_line_pointer;
@@ -4794,59 +5271,57 @@ dot_pred_rel (type)
   SKIP_WHITESPACE ();
   while (1)
     {
-      valueT bit = 1;
+      valueT bits = 1;
       int regno;
-
-      if (TOUPPER (*input_line_pointer) != 'P'
-         || (regno = atoi (++input_line_pointer)) < 0
-         || regno > 63)
-       {
-         as_bad (_("Predicate register expected"));
-         ignore_rest_of_line ();
-         return;
-       }
-      while (ISDIGIT (*input_line_pointer))
-       ++input_line_pointer;
-      if (p1 == -1)
-       p1 = regno;
-      else if (p2 == -1)
-       p2 = regno;
-      bit <<= regno;
-      if (mask & bit)
-       as_warn (_("Duplicate predicate register ignored"));
-      mask |= bit;
-      count++;
-      /* See if it's a range.  */
-      if (*input_line_pointer == '-')
-       {
-         valueT stop = 1;
-         ++input_line_pointer;
-
-         if (TOUPPER (*input_line_pointer) != 'P'
-             || (regno = atoi (++input_line_pointer)) < 0
-             || regno > 63)
-           {
-             as_bad (_("Predicate register expected"));
-             ignore_rest_of_line ();
-             return;
-           }
-         while (ISDIGIT (*input_line_pointer))
-           ++input_line_pointer;
-         stop <<= regno;
-         if (bit >= stop)
+      expressionS pr, *pr1, *pr2;
+
+      expression (&pr);
+      if (pr.X_op == O_register
+         && pr.X_add_number >= REG_P
+         && pr.X_add_number <= REG_P + 63)
+       {
+         regno = pr.X_add_number - REG_P;
+         bits <<= regno;
+         count++;
+         if (p1 == -1)
+           p1 = regno;
+         else if (p2 == -1)
+           p2 = regno;
+       }
+      else if (type != 'i'
+         && pr.X_op == O_subtract
+         && (pr1 = symbol_get_value_expression (pr.X_add_symbol))
+         && pr1->X_op == O_register
+         && pr1->X_add_number >= REG_P
+         && pr1->X_add_number <= REG_P + 63
+         && (pr2 = symbol_get_value_expression (pr.X_op_symbol))
+         && pr2->X_op == O_register
+         && pr2->X_add_number >= REG_P
+         && pr2->X_add_number <= REG_P + 63)
+       {
+         /* It's a range.  */
+         int stop;
+
+         regno = pr1->X_add_number - REG_P;
+         stop = pr2->X_add_number - REG_P;
+         if (regno >= stop)
            {
              as_bad (_("Bad register range"));
              ignore_rest_of_line ();
              return;
            }
-         while (bit < stop)
-           {
-             bit <<= 1;
-             mask |= bit;
-             count++;
-           }
-         SKIP_WHITESPACE ();
+         bits = ((bits << stop) << 1) - (bits << regno);
+         count += stop - regno + 1;
        }
+      else
+       {
+         as_bad (_("Predicate register expected"));
+         ignore_rest_of_line ();
+         return;
+       }
+      if (mask & bits)
+       as_warn (_("Duplicate predicate register ignored"));
+      mask |= bits;
       if (*input_line_pointer != ',')
        break;
       ++input_line_pointer;
@@ -4966,6 +5441,7 @@ const pseudo_typeS md_pseudo_table[] =
   {
     { "radix", dot_radix, 0 },
     { "lcomm", s_lcomm_bytes, 1 },
+    { "loc", dot_loc, 0 },
     { "bss", dot_special_section, SPECIAL_SECTION_BSS },
     { "sbss", dot_special_section, SPECIAL_SECTION_SBSS },
     { "sdata", dot_special_section, SPECIAL_SECTION_SDATA },
@@ -4983,11 +5459,11 @@ const pseudo_typeS md_pseudo_table[] =
     { "fframe", dot_fframe, 0 },
     { "vframe", dot_vframe, 0 },
     { "vframesp", dot_vframesp, 0 },
-    { "vframepsp", dot_vframepsp, 0 },
+    { "vframepsp", dot_vframesp, 1 },
     { "save", dot_save, 0 },
     { "restore", dot_restore, 0 },
     { "restorereg", dot_restorereg, 0 },
-    { "restorereg.p", dot_restorereg_p, 0 },
+    { "restorereg.p", dot_restorereg, 1 },
     { "handlerdata", dot_handlerdata, 0 },
     { "unwentry", dot_unwentry, 0 },
     { "altrp", dot_altrp, 0 },
@@ -5001,16 +5477,13 @@ const pseudo_typeS md_pseudo_table[] =
     { "spillreg", dot_spillreg, 0 },
     { "spillsp", dot_spillmem, 0 },
     { "spillpsp", dot_spillmem, 1 },
-    { "spillreg.p", dot_spillreg_p, 0 },
-    { "spillsp.p", dot_spillmem_p, 0 },
-    { "spillpsp.p", dot_spillmem_p, 1 },
+    { "spillreg.p", dot_spillreg, 1 },
+    { "spillsp.p", dot_spillmem, ~0 },
+    { "spillpsp.p", dot_spillmem, ~1 },
     { "label_state", dot_label_state, 0 },
     { "copy_state", dot_copy_state, 0 },
     { "unwabi", dot_unwabi, 0 },
     { "personality", dot_personality, 0 },
-#if 0
-    { "estate", dot_estate, 0 },
-#endif
     { "mii", dot_template, 0x0 },
     { "mli", dot_template, 0x2 }, /* old format, for compatibility */
     { "mlx", dot_template, 0x2 },
@@ -5022,10 +5495,6 @@ const pseudo_typeS md_pseudo_table[] =
     { "bbb", dot_template, 0xb },
     { "mmb", dot_template, 0xc },
     { "mfb", dot_template, 0xe },
-#if 0
-    { "lb", dot_scope, 0 },
-    { "le", dot_scope, 1 },
-#endif
     { "align", dot_align, 0 },
     { "regstk", dot_regstk, 0 },
     { "rotr", dot_rot, DYNREG_GR },
@@ -5042,6 +5511,7 @@ const pseudo_typeS md_pseudo_table[] =
     { "xdata2", dot_xdata, 2 },
     { "xdata4", dot_xdata, 4 },
     { "xdata8", dot_xdata, 8 },
+    { "xdata16", dot_xdata, 16 },
     { "xreal4", dot_xfloat_cons, 'f' },
     { "xreal8", dot_xfloat_cons, 'd' },
     { "xreal10", dot_xfloat_cons, 'x' },
@@ -5053,6 +5523,7 @@ const pseudo_typeS md_pseudo_table[] =
     { "xdata2.ua", dot_xdata_ua, 2 },
     { "xdata4.ua", dot_xdata_ua, 4 },
     { "xdata8.ua", dot_xdata_ua, 8 },
+    { "xdata16.ua", dot_xdata_ua, 16 },
     { "xreal4.ua", dot_xfloat_cons_ua, 'f' },
     { "xreal8.ua", dot_xfloat_cons_ua, 'd' },
     { "xreal10.ua", dot_xfloat_cons_ua, 'x' },
@@ -5671,7 +6142,7 @@ operand_match (idesc, index, e)
        case O_symbol:
          fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
          /* There are no external relocs for TAG13/TAG13b fields, so we
-            create a dummy reloc.  This will not live past md_apply_fix3.  */
+            create a dummy reloc.  This will not live past md_apply_fix.  */
          fix->code = BFD_RELOC_UNUSED;
          fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code);
          fix->opnd = idesc->operands[index];
@@ -5701,27 +6172,19 @@ operand_match (idesc, index, e)
 }
 
 static int
-parse_operand (e)
+parse_operand (e, more)
      expressionS *e;
+     int more;
 {
   int sep = '\0';
 
   memset (e, 0, sizeof (*e));
   e->X_op = O_absent;
   SKIP_WHITESPACE ();
-  if (*input_line_pointer != '}')
-    expression (e);
-  sep = *input_line_pointer++;
-
-  if (sep == '}')
-    {
-      if (!md.manual_bundling)
-       as_warn ("Found '}' when manual bundling is off");
-      else
-       CURR_SLOT.manual_bundling_off = 1;
-      md.manual_bundling = 0;
-      sep = '\0';
-    }
+  expression (e);
+  sep = *input_line_pointer;
+  if (more && (sep == ',' || sep == more))
+    ++input_line_pointer;
   return sep;
 }
 
@@ -5746,6 +6209,8 @@ parse_operands (idesc)
 {
   int i = 0, highest_unmatched_operand, num_operands = 0, num_outputs = 0;
   int error_pos, out_of_range_pos, curr_out_of_range_pos, sep = 0;
+  int reg1, reg2;
+  char reg_class;
   enum ia64_opnd expected_operand = IA64_OPND_NIL;
   enum operand_match_result result;
   char mnemonic[129];
@@ -5755,7 +6220,8 @@ parse_operands (idesc)
   assert (strlen (idesc->name) <= 128);
 
   strcpy (mnemonic, idesc->name);
-  if (idesc->operands[2] == IA64_OPND_SOF)
+  if (idesc->operands[2] == IA64_OPND_SOF
+      || idesc->operands[1] == IA64_OPND_SOF)
     {
       /* To make the common idiom "alloc loc?=ar.pfs,0,1,0,0" work, we
         can't parse the first operand until we have parsed the
@@ -5773,11 +6239,22 @@ parse_operands (idesc)
       ++num_outputs;
     }
 
-  for (; i < NELEMS (CURR_SLOT.opnd); ++i)
-    {
-      sep = parse_operand (CURR_SLOT.opnd + i);
-      if (CURR_SLOT.opnd[i].X_op == O_absent)
-       break;
+  for (; ; ++i)
+    {
+      if (i < NELEMS (CURR_SLOT.opnd)) 
+       {
+         sep = parse_operand (CURR_SLOT.opnd + i, '=');
+         if (CURR_SLOT.opnd[i].X_op == O_absent)
+           break;
+       }
+      else
+       {
+         expressionS dummy;
+
+         sep = parse_operand (&dummy, '=');
+         if (dummy.X_op == O_absent)
+           break;
+       }
 
       ++num_operands;
 
@@ -5798,44 +6275,55 @@ parse_operands (idesc)
       return 0;
     }
 
-  if (idesc->operands[2] == IA64_OPND_SOF)
+  if (idesc->operands[2] == IA64_OPND_SOF
+      || idesc->operands[1] == IA64_OPND_SOF)
     {
       /* map alloc r1=ar.pfs,i,l,o,r to alloc r1=ar.pfs,(i+l+o),(i+l),r */
       know (strcmp (idesc->name, "alloc") == 0);
-      if (num_operands == 5 /* first_arg not included in this count! */
-         && CURR_SLOT.opnd[2].X_op == O_constant
-         && CURR_SLOT.opnd[3].X_op == O_constant
-         && CURR_SLOT.opnd[4].X_op == O_constant
-         && CURR_SLOT.opnd[5].X_op == O_constant)
-       {
-         sof = set_regstack (CURR_SLOT.opnd[2].X_add_number,
-                             CURR_SLOT.opnd[3].X_add_number,
-                             CURR_SLOT.opnd[4].X_add_number,
-                             CURR_SLOT.opnd[5].X_add_number);
+      i = (CURR_SLOT.opnd[1].X_op == O_register
+          && CURR_SLOT.opnd[1].X_add_number == REG_AR + AR_PFS) ? 2 : 1;
+      if (num_operands == i + 3 /* first_arg not included in this count! */
+         && CURR_SLOT.opnd[i].X_op == O_constant
+         && CURR_SLOT.opnd[i + 1].X_op == O_constant
+         && CURR_SLOT.opnd[i + 2].X_op == O_constant
+         && CURR_SLOT.opnd[i + 3].X_op == O_constant)
+       {
+         sof = set_regstack (CURR_SLOT.opnd[i].X_add_number,
+                             CURR_SLOT.opnd[i + 1].X_add_number,
+                             CURR_SLOT.opnd[i + 2].X_add_number,
+                             CURR_SLOT.opnd[i + 3].X_add_number);
 
          /* now we can parse the first arg:  */
          saved_input_pointer = input_line_pointer;
          input_line_pointer = first_arg;
-         sep = parse_operand (CURR_SLOT.opnd + 0);
+         sep = parse_operand (CURR_SLOT.opnd + 0, '=');
          if (sep != '=')
            --num_outputs;      /* force error */
          input_line_pointer = saved_input_pointer;
 
-         CURR_SLOT.opnd[2].X_add_number = sof;
-         CURR_SLOT.opnd[3].X_add_number
-           = sof - CURR_SLOT.opnd[4].X_add_number;
-         CURR_SLOT.opnd[4] = CURR_SLOT.opnd[5];
+         CURR_SLOT.opnd[i].X_add_number = sof;
+         CURR_SLOT.opnd[i + 1].X_add_number
+           = sof - CURR_SLOT.opnd[i + 2].X_add_number;
+         CURR_SLOT.opnd[i + 2] = CURR_SLOT.opnd[i + 3];
        }
     }
 
-  highest_unmatched_operand = 0;
+  highest_unmatched_operand = -4;
   curr_out_of_range_pos = -1;
   error_pos = 0;
-  expected_operand = idesc->operands[0];
   for (; idesc; idesc = get_next_opcode (idesc))
     {
       if (num_outputs != idesc->num_outputs)
        continue;               /* mismatch in # of outputs */
+      if (highest_unmatched_operand < 0)
+       highest_unmatched_operand |= 1;
+      if (num_operands > NELEMS (idesc->operands)
+         || (num_operands < NELEMS (idesc->operands)
+          && idesc->operands[num_operands])
+         || (num_operands > 0 && !idesc->operands[num_operands - 1]))
+       continue;               /* mismatch in number of arguments */
+      if (highest_unmatched_operand < 0)
+       highest_unmatched_operand |= 2;
 
       CURR_SLOT.num_fixups = 0;
 
@@ -5888,10 +6376,6 @@ parse_operands (idesc)
          continue;
        }
 
-      if (num_operands < NELEMS (idesc->operands)
-         && idesc->operands[num_operands])
-       continue;               /* mismatch in number of arguments */
-
       break;
     }
   if (!idesc)
@@ -5900,103 +6384,136 @@ parse_operands (idesc)
        as_bad ("Operand %u of `%s' should be %s",
                error_pos + 1, mnemonic,
                elf64_ia64_operands[expected_operand].desc);
+      else if (highest_unmatched_operand < 0 && !(highest_unmatched_operand & 1))
+       as_bad ("Wrong number of output operands");
+      else if (highest_unmatched_operand < 0 && !(highest_unmatched_operand & 2))
+       as_bad ("Wrong number of input operands");
       else
        as_bad ("Operand mismatch");
       return 0;
     }
-  return idesc;
-}
-
-/* Keep track of state necessary to determine whether a NOP is necessary
-   to avoid an erratum in A and B step Itanium chips, and return 1 if we
-   detect a case where additional NOPs may be necessary.  */
-static int
-errata_nop_necessary_p (slot, insn_unit)
-     struct slot *slot;
-     enum ia64_unit insn_unit;
-{
-  int i;
-  struct group *this_group = md.last_groups + md.group_idx;
-  struct group *prev_group = md.last_groups + (md.group_idx + 2) % 3;
-  struct ia64_opcode *idesc = slot->idesc;
-
-  /* Test whether this could be the first insn in a problematic sequence.  */
-  if (insn_unit == IA64_UNIT_F)
-    {
-      for (i = 0; i < idesc->num_outputs; i++)
-       if (idesc->operands[i] == IA64_OPND_P1
-           || idesc->operands[i] == IA64_OPND_P2)
-         {
-           int regno = slot->opnd[i].X_add_number - REG_P;
-           /* Ignore invalid operands; they generate errors elsewhere.  */
-           if (regno >= 64)
-             return 0;
-           this_group->p_reg_set[regno] = 1;
-         }
-    }
 
-  /* Test whether this could be the second insn in a problematic sequence.  */
-  if (insn_unit == IA64_UNIT_M && slot->qp_regno > 0
-      && prev_group->p_reg_set[slot->qp_regno])
+  /* Check that the instruction doesn't use
+     - r0, f0, or f1 as output operands
+     - the same predicate twice as output operands
+     - r0 as address of a base update load or store
+     - the same GR as output and address of a base update load
+     - two even- or two odd-numbered FRs as output operands of a floating
+       point parallel load.
+     At most two (conflicting) output (or output-like) operands can exist,
+     (floating point parallel loads have three outputs, but the base register,
+     if updated, cannot conflict with the actual outputs).  */
+  reg2 = reg1 = -1;
+  for (i = 0; i < num_operands; ++i)
     {
-      for (i = 0; i < idesc->num_outputs; i++)
-       if (idesc->operands[i] == IA64_OPND_R1
-           || idesc->operands[i] == IA64_OPND_R2
-           || idesc->operands[i] == IA64_OPND_R3)
-         {
-           int regno = slot->opnd[i].X_add_number - REG_GR;
-           /* Ignore invalid operands; they generate errors elsewhere.  */
-           if (regno >= 128)
-             return 0;
-           if (strncmp (idesc->name, "add", 3) != 0
-               && strncmp (idesc->name, "sub", 3) != 0
-               && strncmp (idesc->name, "shladd", 6) != 0
-               && (idesc->flags & IA64_OPCODE_POSTINC) == 0)
-             this_group->g_reg_set_conditionally[regno] = 1;
-         }
-    }
+      int regno = 0;
 
-  /* Test whether this could be the third insn in a problematic sequence.  */
-  for (i = 0; i < NELEMS (idesc->operands) && idesc->operands[i]; i++)
-    {
-      if (/* For fc, ptc, ptr, tak, thash, tpa, ttag, probe, ptr, ptc.  */
-         idesc->operands[i] == IA64_OPND_R3
-         /* For mov indirect.  */
-         || idesc->operands[i] == IA64_OPND_RR_R3
-         || idesc->operands[i] == IA64_OPND_DBR_R3
-         || idesc->operands[i] == IA64_OPND_IBR_R3
-         || idesc->operands[i] == IA64_OPND_PKR_R3
-         || idesc->operands[i] == IA64_OPND_PMC_R3
-         || idesc->operands[i] == IA64_OPND_PMD_R3
-         || idesc->operands[i] == IA64_OPND_MSR_R3
-         || idesc->operands[i] == IA64_OPND_CPUID_R3
-         /* For itr.  */
-         || idesc->operands[i] == IA64_OPND_ITR_R3
-         || idesc->operands[i] == IA64_OPND_DTR_R3
-         /* Normal memory addresses (load, store, xchg, cmpxchg, etc.).  */
-         || idesc->operands[i] == IA64_OPND_MR3)
-       {
-         int regno = slot->opnd[i].X_add_number - REG_GR;
-         /* Ignore invalid operands; they generate errors elsewhere.  */
-         if (regno >= 128)
-           return 0;
-         if (idesc->operands[i] == IA64_OPND_R3)
-           {
-             if (strcmp (idesc->name, "fc") != 0
-                 && strcmp (idesc->name, "tak") != 0
-                 && strcmp (idesc->name, "thash") != 0
-                 && strcmp (idesc->name, "tpa") != 0
-                 && strcmp (idesc->name, "ttag") != 0
-                 && strncmp (idesc->name, "ptr", 3) != 0
-                 && strncmp (idesc->name, "ptc", 3) != 0
-                 && strncmp (idesc->name, "probe", 5) != 0)
-               return 0;
-           }
-         if (prev_group->g_reg_set_conditionally[regno])
-           return 1;
+      reg_class = 0;
+      switch (idesc->operands[i])
+       {
+       case IA64_OPND_R1:
+       case IA64_OPND_R2:
+       case IA64_OPND_R3:
+         if (i < num_outputs)
+           {
+             if (CURR_SLOT.opnd[i].X_add_number == REG_GR)
+               reg_class = 'r';
+             else if (reg1 < 0)
+               reg1 = CURR_SLOT.opnd[i].X_add_number;
+             else if (reg2 < 0)
+               reg2 = CURR_SLOT.opnd[i].X_add_number;
+           }
+         break;
+       case IA64_OPND_P1:
+       case IA64_OPND_P2:
+         if (i < num_outputs)
+           {
+             if (reg1 < 0)
+               reg1 = CURR_SLOT.opnd[i].X_add_number;
+             else if (reg2 < 0)
+               reg2 = CURR_SLOT.opnd[i].X_add_number;
+           }
+         break;
+       case IA64_OPND_F1:
+       case IA64_OPND_F2:
+       case IA64_OPND_F3:
+       case IA64_OPND_F4:
+         if (i < num_outputs)
+           {
+             if (CURR_SLOT.opnd[i].X_add_number >= REG_FR
+                 && CURR_SLOT.opnd[i].X_add_number <= REG_FR + 1)
+               {
+                 reg_class = 'f';
+                 regno = CURR_SLOT.opnd[i].X_add_number - REG_FR;
+               }
+             else if (reg1 < 0)
+               reg1 = CURR_SLOT.opnd[i].X_add_number;
+             else if (reg2 < 0)
+               reg2 = CURR_SLOT.opnd[i].X_add_number;
+           }
+         break;
+       case IA64_OPND_MR3:
+         if (idesc->flags & IA64_OPCODE_POSTINC)
+           {
+             if (CURR_SLOT.opnd[i].X_add_number == REG_GR)
+               reg_class = 'm';
+             else if (reg1 < 0)
+               reg1 = CURR_SLOT.opnd[i].X_add_number;
+             else if (reg2 < 0)
+               reg2 = CURR_SLOT.opnd[i].X_add_number;
+           }
+         break;
+       default:
+         break;
+       }
+      switch (reg_class)
+       {
+       case 0:
+         break;
+       default:
+         as_warn ("Invalid use of `%c%d' as output operand", reg_class, regno);
+         break;
+       case 'm':
+         as_warn ("Invalid use of `r%d' as base update address operand", regno);
+         break;
        }
     }
-  return 0;
+  if (reg1 == reg2)
+    {
+      if (reg1 >= REG_GR && reg1 <= REG_GR + 127)
+       {
+         reg1 -= REG_GR;
+         reg_class = 'r';
+       }
+      else if (reg1 >= REG_P && reg1 <= REG_P + 63)
+       {
+         reg1 -= REG_P;
+         reg_class = 'p';
+       }
+      else if (reg1 >= REG_FR && reg1 <= REG_FR + 127)
+       {
+         reg1 -= REG_FR;
+         reg_class = 'f';
+       }
+      else
+       reg_class = 0;
+      if (reg_class)
+       as_warn ("Invalid duplicate use of `%c%d'", reg_class, reg1);
+    }
+  else if (((reg1 >= REG_FR && reg1 <= REG_FR + 31
+            && reg2 >= REG_FR && reg2 <= REG_FR + 31)
+           || (reg1 >= REG_FR + 32 && reg1 <= REG_FR + 127
+            && reg2 >= REG_FR + 32 && reg2 <= REG_FR + 127))
+          && ! ((reg1 ^ reg2) & 1))
+    as_warn ("Invalid simultaneous use of `f%d' and `f%d'",
+            reg1 - REG_FR, reg2 - REG_FR);
+  else if ((reg1 >= REG_FR && reg1 <= REG_FR + 31
+           && reg2 >= REG_FR + 32 && reg2 <= REG_FR + 127)
+          || (reg1 >= REG_FR + 32 && reg1 <= REG_FR + 127
+           && reg2 >= REG_FR && reg2 <= REG_FR + 31))
+    as_warn ("Dangerous simultaneous use of `f%d' and `f%d'",
+            reg1 - REG_FR, reg2 - REG_FR);
+  return idesc;
 }
 
 static void
@@ -6006,7 +6523,8 @@ build_insn (slot, insnp)
 {
   const struct ia64_operand *odesc, *o2desc;
   struct ia64_opcode *idesc = slot->idesc;
-  bfd_signed_vma insn, val;
+  bfd_vma insn;
+  bfd_signed_vma val;
   const char *err;
   int i;
 
@@ -6126,18 +6644,17 @@ build_insn (slot, insnp)
 static void
 emit_one_bundle ()
 {
-  unsigned int manual_bundling_on = 0, manual_bundling_off = 0;
-  unsigned int manual_bundling = 0;
+  int manual_bundling_off = 0, manual_bundling = 0;
   enum ia64_unit required_unit, insn_unit = 0;
   enum ia64_insn_type type[3], insn_type;
   unsigned int template, orig_template;
   bfd_vma insn[3] = { -1, -1, -1 };
   struct ia64_opcode *idesc;
   int end_of_insn_group = 0, user_template = -1;
-  int n, i, j, first, curr;
-  unw_rec_list *ptr, *last_ptr, *end_ptr;
+  int n, i, j, first, curr, last_slot;
   bfd_vma t0 = 0, t1 = 0;
   struct label_fix *lfix;
+  bfd_boolean mark_label;
   struct insn_fix *ifix;
   char mnemonic[16];
   fixS *fix;
@@ -6186,10 +6703,13 @@ emit_one_bundle ()
   curr = first;
   idesc = md.slot[curr].idesc;
   end_of_insn_group = 0;
+  last_slot = -1;
   for (i = 0; i < 3 && md.num_slots_in_use > 0; ++i)
     {
       /* If we have unwind records, we may need to update some now.  */
-      ptr = md.slot[curr].unwind_record;
+      unw_rec_list *ptr = md.slot[curr].unwind_record;
+      unw_rec_list *end_ptr = NULL;
+
       if (ptr)
        {
          /* Find the last prologue/body record in the list for the current
@@ -6199,9 +6719,11 @@ emit_one_bundle ()
             issued.  This matters because there may have been nops emitted
             meanwhile.  Any non-prologue non-body record followed by a
             prologue/body record must also refer to the current point.  */
-         last_ptr = NULL;
-         end_ptr = md.slot[(curr + 1) % NUM_SLOTS].unwind_record;
-         for (; ptr != end_ptr; ptr = ptr->next)
+         unw_rec_list *last_ptr;
+
+         for (j = 1; end_ptr == NULL && j < md.num_slots_in_use; ++j)
+           end_ptr = md.slot[(curr + j) % NUM_SLOTS].unwind_record;
+         for (last_ptr = NULL; ptr != end_ptr; ptr = ptr->next)
            if (ptr->r.type == prologue || ptr->r.type == prologue_gr
                || ptr->r.type == body)
              last_ptr = ptr;
@@ -6222,13 +6744,30 @@ emit_one_bundle ()
            }
        }
 
-      if (idesc->flags & IA64_OPCODE_SLOT2)
+      manual_bundling_off = md.slot[curr].manual_bundling_off;
+      if (md.slot[curr].manual_bundling_on)
        {
-         if (manual_bundling && i != 2)
-           as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
-                         "`%s' must be last in bundle", idesc->name);
+         if (curr == first)
+           manual_bundling = 1;
          else
-           i = 2;
+         break; /* Need to start a new bundle.  */
+       }
+
+      /* If this instruction specifies a template, then it must be the first
+        instruction of a bundle.  */
+      if (curr != first && md.slot[curr].user_template >= 0)
+       break;
+
+      if (idesc->flags & IA64_OPCODE_SLOT2)
+       {
+         if (manual_bundling && !manual_bundling_off)
+           {
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                           "`%s' must be last in bundle", idesc->name);
+             if (i < 2)
+               manual_bundling = -1; /* Suppress meaningless post-loop errors.  */
+           }
+         i = 2;
        }
       if (idesc->flags & IA64_OPCODE_LAST)
        {
@@ -6261,10 +6800,19 @@ emit_one_bundle ()
              required_slot = i;
              break;
            }
-         if (manual_bundling && i != required_slot)
-           as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
-                         "`%s' must be last in instruction group",
-                         idesc->name);
+         if (manual_bundling
+             && (i > required_slot
+                 || (required_slot == 2 && !manual_bundling_off)
+                 || (user_template >= 0
+                     /* Changing from MMI to M;MI is OK.  */
+                     && (template ^ required_template) > 1)))
+           {
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                           "`%s' must be last in instruction group",
+                           idesc->name);
+             if (i < 2 && required_slot == 2 && !manual_bundling_off)
+               manual_bundling = -1; /* Suppress meaningless post-loop errors.  */
+           }
          if (required_slot < i)
            /* Can't fit this instruction.  */
            break;
@@ -6283,24 +6831,16 @@ emit_one_bundle ()
        }
       if (curr != first && md.slot[curr].label_fixups)
        {
-         if (manual_bundling_on)
-           as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+         if (manual_bundling)
+           {
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
                          "Label must be first in a bundle");
+             manual_bundling = -1; /* Suppress meaningless post-loop errors.  */
+           }
          /* This insn must go into the first slot of a bundle.  */
          break;
        }
 
-      manual_bundling_on = md.slot[curr].manual_bundling_on;
-      manual_bundling_off = md.slot[curr].manual_bundling_off;
-
-      if (manual_bundling_on)
-       {
-         if (curr == first)
-           manual_bundling = 1;
-         else
-           break;                      /* need to start a new bundle */
-       }
-
       if (end_of_insn_group && md.num_slots_in_use >= 1)
        {
          /* We need an instruction group boundary in the middle of a
@@ -6328,12 +6868,17 @@ emit_one_bundle ()
                      reason we have to check for this is that otherwise we
                      may end up generating "MI;;I M.." which has the deadly
                      effect that the second M instruction is no longer the
-                     first in the bundle! --davidm 99/12/16  */
+                     first in the group! --davidm 99/12/16  */
                   && (idesc->flags & IA64_OPCODE_FIRST) == 0)
            {
              template = 1;
              end_of_insn_group = 0;
            }
+         else if (i == 1
+                  && user_template == 0
+                  && !(idesc->flags & IA64_OPCODE_FIRST))
+           /* Use the next slot.  */
+           continue;
          else if (curr != first)
            /* can't fit this insn */
            break;
@@ -6351,25 +6896,62 @@ emit_one_bundle ()
       /* resolve dynamic opcodes such as "break", "hint", and "nop":  */
       if (idesc->type == IA64_TYPE_DYN)
        {
+         enum ia64_opnd opnd1, opnd2;
+
          if ((strcmp (idesc->name, "nop") == 0)
-             || (strcmp (idesc->name, "hint") == 0)
              || (strcmp (idesc->name, "break") == 0))
            insn_unit = required_unit;
-         else if (strcmp (idesc->name, "chk.s") == 0)
+         else if (strcmp (idesc->name, "hint") == 0)
+           {
+             insn_unit = required_unit;
+             if (required_unit == IA64_UNIT_B)
+               {
+                 switch (md.hint_b)
+                   {
+                   case hint_b_ok:
+                     break;
+                   case hint_b_warning:
+                     as_warn ("hint in B unit may be treated as nop");
+                     break;
+                   case hint_b_error:
+                     /* When manual bundling is off and there is no
+                        user template, we choose a different unit so
+                        that hint won't go into the current slot. We
+                        will fill the current bundle with nops and
+                        try to put hint into the next bundle.  */
+                     if (!manual_bundling && user_template < 0)
+                       insn_unit = IA64_UNIT_I;
+                     else
+                       as_bad ("hint in B unit can't be used");
+                     break;
+                   }
+               }
+           }
+         else if (strcmp (idesc->name, "chk.s") == 0
+             || strcmp (idesc->name, "mov") == 0)
            {
              insn_unit = IA64_UNIT_M;
-             if (required_unit == IA64_UNIT_I)
+             if (required_unit == IA64_UNIT_I
+                 || (required_unit == IA64_UNIT_F && template == 6))
                insn_unit = IA64_UNIT_I;
            }
          else
            as_fatal ("emit_one_bundle: unexpected dynamic op");
 
-         sprintf (mnemonic, "%s.%c", idesc->name, "?imbf??"[insn_unit]);
+         sprintf (mnemonic, "%s.%c", idesc->name, "?imbfxx"[insn_unit]);
+         opnd1 = idesc->operands[0];
+         opnd2 = idesc->operands[1];
          ia64_free_opcode (idesc);
-         md.slot[curr].idesc = idesc = ia64_find_opcode (mnemonic);
-#if 0
-         know (!idesc->next);  /* no resolved dynamic ops have collisions */
-#endif
+         idesc = ia64_find_opcode (mnemonic);
+         /* moves to/from ARs have collisions */
+         if (opnd1 == IA64_OPND_AR3 || opnd2 == IA64_OPND_AR3)
+           {
+             while (idesc != NULL
+                    && (idesc->operands[0] != opnd1
+                        || idesc->operands[1] != opnd2))
+               idesc = get_next_opcode (idesc);
+           }
+         md.slot[curr].idesc = idesc;
        }
       else
        {
@@ -6391,37 +6973,34 @@ emit_one_bundle ()
        }
 
       if (insn_unit != required_unit)
+       continue;               /* Try next slot.  */
+
+      /* Now is a good time to fix up the labels for this insn.  */
+      mark_label = FALSE;
+      for (lfix = md.slot[curr].label_fixups; lfix; lfix = lfix->next)
        {
-         if (required_unit == IA64_UNIT_L
-             && insn_unit == IA64_UNIT_I
-             && !(idesc->flags & IA64_OPCODE_X_IN_MLX))
-           {
-             /* we got ourselves an MLX template but the current
-                instruction isn't an X-unit, or an I-unit instruction
-                that can go into the X slot of an MLX template.  Duh.  */
-             if (md.num_slots_in_use >= NUM_SLOTS)
-               {
-                 as_bad_where (md.slot[curr].src_file,
-                               md.slot[curr].src_line,
-                               "`%s' can't go in X slot of "
-                               "MLX template", idesc->name);
-                 /* drop this insn so we don't livelock:  */
-                 --md.num_slots_in_use;
-               }
-             break;
-           }
-         continue;             /* try next slot */
+         S_SET_VALUE (lfix->sym, frag_now_fix () - 16);
+         symbol_set_frag (lfix->sym, frag_now);
+         mark_label |= lfix->dw2_mark_labels;
+       }
+      for (lfix = md.slot[curr].tag_fixups; lfix; lfix = lfix->next)
+       {
+         S_SET_VALUE (lfix->sym, frag_now_fix () - 16 + i);
+         symbol_set_frag (lfix->sym, frag_now);
        }
 
-      {
-       bfd_vma addr;
+      if (debug_type == DEBUG_DWARF2
+         || md.slot[curr].loc_directive_seen
+         || mark_label)
+       {
+         bfd_vma addr = frag_now->fr_address + frag_now_fix () - 16 + i;
 
-       addr = frag_now->fr_address + frag_now_fix () - 16 + i;
-       dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
-      }
+         md.slot[curr].loc_directive_seen = 0;
+         if (mark_label)
+           md.slot[curr].debug_line.flags |= DWARF2_FLAG_BASIC_BLOCK;
 
-      if (errata_nop_necessary_p (md.slot + curr, insn_unit))
-       as_warn (_("Additional NOP may be necessary to workaround Itanium processor A/B step errata"));
+         dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
+       }
 
       build_insn (md.slot + curr, insn + i);
 
@@ -6431,7 +7010,6 @@ emit_one_bundle ()
          /* Set slot numbers for all remaining unwind records belonging to the
             current insn.  There can not be any prologue/body unwind records
             here.  */
-         end_ptr = md.slot[(curr + 1) % NUM_SLOTS].unwind_record;
          for (; ptr != end_ptr; ptr = ptr->next)
            {
              ptr->slot_number = (unsigned long) f + i;
@@ -6447,19 +7025,7 @@ emit_one_bundle ()
          ++i;
        }
       --md.num_slots_in_use;
-
-      /* now is a good time to fix up the labels for this insn:  */
-      for (lfix = md.slot[curr].label_fixups; lfix; lfix = lfix->next)
-       {
-         S_SET_VALUE (lfix->sym, frag_now_fix () - 16);
-         symbol_set_frag (lfix->sym, frag_now);
-       }
-      /* and fix up the tags also.  */
-      for (lfix = md.slot[curr].tag_fixups; lfix; lfix = lfix->next)
-       {
-         S_SET_VALUE (lfix->sym, frag_now_fix () - 16 + i);
-         symbol_set_frag (lfix->sym, frag_now);
-       }
+      last_slot = i;
 
       for (j = 0; j < md.slot[curr].num_fixups; ++j)
        {
@@ -6474,12 +7040,6 @@ emit_one_bundle ()
 
       end_of_insn_group = md.slot[curr].end_of_insn_group;
 
-      if (end_of_insn_group)
-       {
-         md.group_idx = (md.group_idx + 1) % 3;
-         memset (md.last_groups + md.group_idx, 0, sizeof md.last_groups[0]);
-       }
-
       /* clear slot:  */
       ia64_free_opcode (md.slot[curr].idesc);
       memset (md.slot + curr, 0, sizeof (md.slot[curr]));
@@ -6493,12 +7053,40 @@ emit_one_bundle ()
       curr = (curr + 1) % NUM_SLOTS;
       idesc = md.slot[curr].idesc;
     }
-  if (manual_bundling)
+  if (manual_bundling > 0)
     {
       if (md.num_slots_in_use > 0)
-       as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
-                     "`%s' does not fit into %s template",
-                     idesc->name, ia64_templ_desc[template].name);
+       {
+         if (last_slot >= 2)
+           as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                         "`%s' does not fit into bundle", idesc->name);
+         else if (last_slot < 0)
+           {
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                           "`%s' does not fit into %s template",
+                           idesc->name, ia64_templ_desc[template].name);
+             /* Drop first insn so we don't livelock.  */
+             --md.num_slots_in_use;
+             know (curr == first);
+             ia64_free_opcode (md.slot[curr].idesc);
+             memset (md.slot + curr, 0, sizeof (md.slot[curr]));
+             md.slot[curr].user_template = -1;
+           }
+         else
+           {
+             const char *where;
+
+             if (template == 2)
+               where = "X slot";
+             else if (last_slot == 0)
+               where = "slots 2 or 3";
+             else
+               where = "slot 3";
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                           "`%s' can't go in %s of %s template",
+                           idesc->name, where, ia64_templ_desc[template].name);
+           }
+       }
       else
        as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
                      "Missing '}' at end of file");
@@ -6510,12 +7098,6 @@ emit_one_bundle ()
 
   number_to_chars_littleendian (f + 0, t0, 8);
   number_to_chars_littleendian (f + 8, t1, 8);
-
-  if (unwind.list)
-    {
-      unwind.list->next_slot_number = (unsigned long) f + 16;
-      unwind.list->next_slot_frag = frag_now;
-    }
 }
 
 int
@@ -6548,6 +7130,38 @@ md_parse_option (c, arg)
          md.flags |= EF_IA_64_BE;
          default_big_endian = 1;
        }
+      else if (strncmp (arg, "unwind-check=", 13) == 0)
+       {
+         arg += 13;
+         if (strcmp (arg, "warning") == 0)
+           md.unwind_check = unwind_check_warning;
+         else if (strcmp (arg, "error") == 0)
+           md.unwind_check = unwind_check_error;
+         else
+           return 0;
+       }
+      else if (strncmp (arg, "hint.b=", 7) == 0)
+       {
+         arg += 7;
+         if (strcmp (arg, "ok") == 0)
+           md.hint_b = hint_b_ok;
+         else if (strcmp (arg, "warning") == 0)
+           md.hint_b = hint_b_warning;
+         else if (strcmp (arg, "error") == 0)
+           md.hint_b = hint_b_error;
+         else
+           return 0;
+       }
+      else if (strncmp (arg, "tune=", 5) == 0)
+       {
+         arg += 5;
+         if (strcmp (arg, "itanium1") == 0)
+           md.tune = itanium1;
+         else if (strcmp (arg, "itanium2") == 0)
+           md.tune = itanium2;
+         else
+           return 0;
+       }
       else
        return 0;
       break;
@@ -6603,6 +7217,10 @@ md_parse_option (c, arg)
        {
          md.default_explicit_mode = 0;
        }
+      else if (strcmp (arg, "none") == 0)
+       {
+         md.detect_dv = 0;
+       }
       else if (strcmp (arg, "debug") == 0)
        {
          md.debug_dv = 1;
@@ -6612,6 +7230,11 @@ md_parse_option (c, arg)
          md.default_explicit_mode = 1;
          md.debug_dv = 1;
        }
+      else if (strcmp (arg, "debugn") == 0)
+       {
+         md.debug_dv = 1;
+         md.detect_dv = 0;
+       }
       else
        {
          as_bad (_("Unrecognized option '-x%s'"), arg);
@@ -6651,9 +7274,20 @@ IA-64 options:\n\
                          EF_IA_64_NOFUNCDESC_CONS_GP)\n\
   -milp32|-milp64|-mlp64|-mp64 select data model (default -mlp64)\n\
   -mle | -mbe            select little- or big-endian byte order (default -mle)\n\
-  -x | -xexplicit        turn on dependency violation checking (default)\n\
-  -xauto                 automagically remove dependency violations\n\
-  -xdebug                debug dependency violation checker\n"),
+  -mtune=[itanium1|itanium2]\n\
+                         tune for a specific CPU (default -mtune=itanium2)\n\
+  -munwind-check=[warning|error]\n\
+                         unwind directive check (default -munwind-check=warning)\n\
+  -mhint.b=[ok|warning|error]\n\
+                         hint.b check (default -mhint.b=error)\n\
+  -x | -xexplicit        turn on dependency violation checking\n\
+  -xauto                 automagically remove dependency violations (default)\n\
+  -xnone                 turn off dependency violation checking\n\
+  -xdebug                debug dependency violation checker\n\
+  -xdebugn               debug dependency violation checker but turn off\n\
+                         dependency violation checking\n\
+  -xdebugx               debug dependency violation checker and turn on\n\
+                         dependency violation checking\n"),
        stream);
 }
 
@@ -6689,17 +7323,37 @@ match (int templ, int type, int slot)
   return result;
 }
 
-/* Add a bit of extra goodness if a nop of type F or B would fit
-   in TEMPL at SLOT.  */
+/* For Itanium 1, add a bit of extra goodness if a nop of type F or B would fit
+   in TEMPL at SLOT.  For Itanium 2, add a bit of extra goodness if a nop of
+   type M or I would fit in TEMPL at SLOT.  */
 
 static inline int
 extra_goodness (int templ, int slot)
 {
-  if (slot == 1 && match (templ, IA64_TYPE_F, slot))
-    return 2;
-  if (slot == 2 && match (templ, IA64_TYPE_B, slot))
-    return 1;
-  return 0;
+  switch (md.tune)
+    {
+    case itanium1:
+      if (slot == 1 && match (templ, IA64_TYPE_F, slot))
+       return 2;
+      else if (slot == 2 && match (templ, IA64_TYPE_B, slot))
+       return 1;
+      else
+       return 0;
+      break;
+    case itanium2:
+      if (match (templ, IA64_TYPE_M, slot)
+         || match (templ, IA64_TYPE_I, slot))
+       /* Favor M- and I-unit NOPs.  We definitely want to avoid
+          F-unit and B-unit may cause split-issue or less-than-optimal
+          branch-prediction.  */
+       return 2;
+      else
+       return 0;
+      break;
+    default:
+      abort ();
+      return 0;
+    }
 }
 
 /* This function is called once, at assembler startup time.  It sets
@@ -6794,11 +7448,17 @@ md_begin ()
     symbol_new (".<iplt>", undefined_section, FUNC_IPLT_RELOC,
                &zero_address_frag);
 
+ if (md.tune != itanium1)
+   {
+     /* Convert MFI NOPs bundles into MMI NOPs bundles.  */
+     le_nop[0] = 0x8;
+     le_nop_stop[0] = 0x9;
+   }
+
   /* Compute the table of best templates.  We compute goodness as a
-     base 4 value, in which each match counts for 3, each F counts
-     for 2, each B counts for 1.  This should maximize the number of
-     F and B nops in the chosen bundles, which is good because these
-     pipelines are least likely to be overcommitted.  */
+     base 4 value, in which each match counts for 3.  Match-failures
+     result in NOPs and we use extra_goodness() to pick the execution
+     units that are best suited for issuing the NOP.  */
   for (i = 0; i < IA64_NUM_TYPES; ++i)
     for (j = 0; j < IA64_NUM_TYPES; ++j)
       for (k = 0; k < IA64_NUM_TYPES; ++k)
@@ -6811,7 +7471,7 @@ md_begin ()
                {
                  if (match (t, j, 1))
                    {
-                     if (match (t, k, 2))
+                     if ((t == 2 && j == IA64_TYPE_X) || match (t, k, 2))
                        goodness = 3 + 3 + 3;
                      else
                        goodness = 3 + 3 + extra_goodness (t, 2);
@@ -6827,7 +7487,7 @@ md_begin ()
                }
              else if (match (t, i, 1))
                {
-                 if (match (t, j, 2))
+                 if ((t == 2 && i == IA64_TYPE_X) || match (t, j, 2))
                    goodness = 3 + 3;
                  else
                    goodness = 3 + extra_goodness (t, 2);
@@ -6843,6 +7503,21 @@ md_begin ()
            }
        }
 
+#ifdef DEBUG_TEMPLATES
+  /* For debugging changes to the best_template calculations.  We don't care
+     about combinations with invalid instructions, so start the loops at 1.  */
+  for (i = 0; i < IA64_NUM_TYPES; ++i)
+    for (j = 0; j < IA64_NUM_TYPES; ++j)
+      for (k = 0; k < IA64_NUM_TYPES; ++k)
+       {
+         char type_letter[IA64_NUM_TYPES] = { 'n', 'a', 'i', 'm', 'b', 'f',
+                                              'x', 'd' };
+         fprintf (stderr, "%c%c%c %s\n", type_letter[i], type_letter[j],
+                  type_letter[k],
+                  ia64_templ_desc[best_template[i][j][k]].name);
+       }
+#endif
+
   for (i = 0; i < NUM_SLOTS; ++i)
     md.slot[i].user_template = -1;
 
@@ -6985,10 +7660,9 @@ md_begin ()
   md.entry_labels = NULL;
 }
 
-/* Set the elf type to 64 bit ABI by default.  Cannot do this in md_begin
-   because that is called after md_parse_option which is where we do the
-   dynamic changing of md.flags based on -mlp64 or -milp32.  Also, set the
-   default endianness.  */
+/* Set the default options in md.  Cannot do this in md_begin because
+   that is called after md_parse_option which is where we set the
+   options in md based on command line options.  */
 
 void
 ia64_init (argc, argv)
@@ -6996,6 +7670,11 @@ ia64_init (argc, argv)
      char **argv ATTRIBUTE_UNUSED;
 {
   md.flags = MD_FLAGS_DEFAULT;
+  md.detect_dv = 1;
+  /* FIXME: We should change it to unwind_check_error someday.  */
+  md.unwind_check = unwind_check_warning;
+  md.hint_b = hint_b_error;
+  md.tune = itanium2;
 }
 
 /* Return a string for the target object file format.  */
@@ -7061,6 +7740,15 @@ ia64_end_of_source ()
 void
 ia64_start_line ()
 {
+  static int first;
+
+  if (!first) {
+    /* Make sure we don't reference input_line_pointer[-1] when that's
+       not valid.  */
+    first = 1;
+    return;
+  }
+
   if (md.qp.X_op == O_register)
     as_bad ("qualifying predicate not followed by instruction");
   md.qp.X_op = O_absent;
@@ -7071,42 +7759,20 @@ ia64_start_line ()
   if (input_line_pointer[0] == ';' && input_line_pointer[-1] == ';')
     {
       if (md.detect_dv && !md.explicit_mode)
-       as_warn (_("Explicit stops are ignored in auto mode"));
+       {
+         static int warned;
+
+         if (!warned)
+           {
+             warned = 1;
+             as_warn (_("Explicit stops are ignored in auto mode"));
+           }
+       }
       else
        insn_group_break (1, 0, 0);
     }
-}
-
-/* This is a hook for ia64_frob_label, so that it can distinguish tags from
-   labels.  */
-static int defining_tag = 0;
-
-int
-ia64_unrecognized_line (ch)
-     int ch;
-{
-  switch (ch)
-    {
-    case '(':
-      expression (&md.qp);
-      if (*input_line_pointer++ != ')')
-       {
-         as_bad ("Expected ')'");
-         return 0;
-       }
-      if (md.qp.X_op != O_register)
-       {
-         as_bad ("Qualifying predicate expected");
-         return 0;
-       }
-      if (md.qp.X_add_number < REG_P || md.qp.X_add_number >= REG_P + 64)
-       {
-         as_bad ("Predicate register expected");
-         return 0;
-       }
-      return 1;
-
-    case '{':
+  else if (input_line_pointer[-1] == '{')
+    {
       if (md.manual_bundling)
        as_warn ("Found '{' when manual bundling is already turned on");
       else
@@ -7123,9 +7789,9 @@ ia64_unrecognized_line (ch)
          else
            as_warn (_("Found '{' after explicit switch to automatic mode"));
        }
-      return 1;
-
-    case '}':
+    }
+  else if (input_line_pointer[-1] == '}')
+    {
       if (!md.manual_bundling)
        as_warn ("Found '}' when manual bundling is off");
       else
@@ -7138,17 +7804,36 @@ ia64_unrecognized_line (ch)
          && !md.mode_explicitly_set
          && !md.default_explicit_mode)
        dot_dv_mode ('A');
+    }
+}
 
-      /* Allow '{' to follow on the same line.  We also allow ";;", but that
-        happens automatically because ';' is an end of line marker.  */
-      SKIP_WHITESPACE ();
-      if (input_line_pointer[0] == '{')
+/* This is a hook for ia64_frob_label, so that it can distinguish tags from
+   labels.  */
+static int defining_tag = 0;
+
+int
+ia64_unrecognized_line (ch)
+     int ch;
+{
+  switch (ch)
+    {
+    case '(':
+      expression (&md.qp);
+      if (*input_line_pointer++ != ')')
        {
-         input_line_pointer++;
-         return ia64_unrecognized_line ('{');
+         as_bad ("Expected ')'");
+         return 0;
+       }
+      if (md.qp.X_op != O_register)
+       {
+         as_bad ("Qualifying predicate expected");
+         return 0;
+       }
+      if (md.qp.X_add_number < REG_P || md.qp.X_add_number >= REG_P + 64)
+       {
+         as_bad ("Predicate register expected");
+         return 0;
        }
-
-      demand_empty_rest_of_line ();
       return 1;
 
     case '[':
@@ -7233,6 +7918,7 @@ ia64_frob_label (sym)
       fix = obstack_alloc (&notes, sizeof (*fix));
       fix->sym = sym;
       fix->next = CURR_SLOT.tag_fixups;
+      fix->dw2_mark_labels = FALSE;
       CURR_SLOT.tag_fixups = fix;
 
       return;
@@ -7244,6 +7930,7 @@ ia64_frob_label (sym)
       fix = obstack_alloc (&notes, sizeof (*fix));
       fix->sym = sym;
       fix->next = CURR_SLOT.label_fixups;
+      fix->dw2_mark_labels = dwarf2_loc_mark_labels;
       CURR_SLOT.label_fixups = fix;
 
       /* Keep track of how many code entry points we've seen.  */
@@ -7334,16 +8021,100 @@ ia64_optimize_expr (l, op, r)
 }
 
 int
-ia64_parse_name (name, e)
+ia64_parse_name (name, e, nextcharP)
      char *name;
      expressionS *e;
+     char *nextcharP;
 {
   struct const_desc *cdesc;
   struct dynreg *dr = 0;
-  unsigned int regnum;
+  unsigned int idx;
   struct symbol *sym;
   char *end;
 
+  if (*name == '@')
+    {
+      enum pseudo_type pseudo_type = PSEUDO_FUNC_NONE;
+
+      /* Find what relocation pseudo-function we're dealing with.  */
+      for (idx = 0; idx < NELEMS (pseudo_func); ++idx)
+       if (pseudo_func[idx].name
+           && pseudo_func[idx].name[0] == name[1]
+           && strcmp (pseudo_func[idx].name + 1, name + 2) == 0)
+         {
+           pseudo_type = pseudo_func[idx].type;
+           break;
+         }
+      switch (pseudo_type)
+       {
+       case PSEUDO_FUNC_RELOC:
+         end = input_line_pointer;
+         if (*nextcharP != '(')
+           {
+             as_bad ("Expected '('");
+             break;
+           }
+         /* Skip '('.  */
+         ++input_line_pointer;
+         expression (e);
+         if (*input_line_pointer != ')')
+           {
+             as_bad ("Missing ')'");
+             goto done;
+           }
+         /* Skip ')'.  */
+         ++input_line_pointer;
+         if (e->X_op != O_symbol)
+           {
+             if (e->X_op != O_pseudo_fixup)
+               {
+                 as_bad ("Not a symbolic expression");
+                 goto done;
+               }
+             if (idx != FUNC_LT_RELATIVE)
+               {
+                 as_bad ("Illegal combination of relocation functions");
+                 goto done;
+               }
+             switch (S_GET_VALUE (e->X_op_symbol))
+               {
+               case FUNC_FPTR_RELATIVE:
+                 idx = FUNC_LT_FPTR_RELATIVE; break;
+               case FUNC_DTP_MODULE:
+                 idx = FUNC_LT_DTP_MODULE; break;
+               case FUNC_DTP_RELATIVE:
+                 idx = FUNC_LT_DTP_RELATIVE; break;
+               case FUNC_TP_RELATIVE:
+                 idx = FUNC_LT_TP_RELATIVE; break;
+               default:
+                 as_bad ("Illegal combination of relocation functions");
+                 goto done;
+               }
+           }
+         /* Make sure gas doesn't get rid of local symbols that are used
+            in relocs.  */
+         e->X_op = O_pseudo_fixup;
+         e->X_op_symbol = pseudo_func[idx].u.sym;
+       done:
+         *nextcharP = *input_line_pointer;
+         break;
+
+       case PSEUDO_FUNC_CONST:
+         e->X_op = O_constant;
+         e->X_add_number = pseudo_func[idx].u.ival;
+         break;
+
+       case PSEUDO_FUNC_REG:
+         e->X_op = O_register;
+         e->X_add_number = pseudo_func[idx].u.ival;
+         break;
+
+       default:
+         return 0;
+       }
+      return 1;
+    }
+
   /* first see if NAME is a known register name:  */
   sym = hash_find (md.reg_hash, name);
   if (sym)
@@ -7362,13 +8133,14 @@ ia64_parse_name (name, e)
     }
 
   /* check for inN, locN, or outN:  */
+  idx = 0;
   switch (name[0])
     {
     case 'i':
       if (name[1] == 'n' && ISDIGIT (name[2]))
        {
          dr = &md.in;
-         name += 2;
+         idx = 2;
        }
       break;
 
@@ -7376,7 +8148,7 @@ ia64_parse_name (name, e)
       if (name[1] == 'o' && name[2] == 'c' && ISDIGIT (name[3]))
        {
          dr = &md.loc;
-         name += 3;
+         idx = 3;
        }
       break;
 
@@ -7384,7 +8156,7 @@ ia64_parse_name (name, e)
       if (name[1] == 'u' && name[2] == 't' && ISDIGIT (name[3]))
        {
          dr = &md.out;
-         name += 3;
+         idx = 3;
        }
       break;
 
@@ -7392,13 +8164,16 @@ ia64_parse_name (name, e)
       break;
     }
 
-  if (dr)
+  /* Ignore register numbers with leading zeroes, except zero itself.  */
+  if (dr && (name[idx] != '0' || name[idx + 1] == '\0'))
     {
+      unsigned long regnum;
+
       /* The name is inN, locN, or outN; parse the register number.  */
-      regnum = strtoul (name, &end, 10);
-      if (end > name && *end == '\0')
+      regnum = strtoul (name + idx, &end, 10);
+      if (end > name + idx && *end == '\0' && regnum < 96)
        {
-         if ((unsigned) regnum >= dr->num_regs)
+         if (regnum >= dr->num_regs)
            {
              if (!dr->num_regs)
                as_bad ("No current frame");
@@ -7413,6 +8188,9 @@ ia64_parse_name (name, e)
        }
     }
 
+  end = alloca (strlen (name) + 1);
+  strcpy (end, name);
+  name = ia64_canonicalize_symbol_name (end);
   if ((dr = hash_find (md.dynreg_hash, name)))
     {
       /* We've got ourselves the name of a rotating register set.
@@ -7432,9 +8210,18 @@ char *
 ia64_canonicalize_symbol_name (name)
      char *name;
 {
-  size_t len = strlen (name);
-  if (len > 1 && name[len - 1] == '#')
-    name[len - 1] = '\0';
+  size_t len = strlen (name), full = len;
+
+  while (len > 0 && name[len - 1] == '#')
+    --len;
+  if (len <= 0)
+    {
+      if (full > 0)
+       as_bad ("Standalone `#' is illegal");
+    }
+  else if (len < full - 1)
+    as_warn ("Redundant `#' suffix operators");
+  name[len] = '\0';
   return name;
 }
 
@@ -7580,9 +8367,15 @@ specify_resource (dep, idesc, type, specs, note, path)
   tmpl.qp_regno = CURR_SLOT.qp_regno;
   tmpl.link_to_qp_branch = 1;
   tmpl.mem_offset.hint = 0;
+  tmpl.mem_offset.offset = 0;
+  tmpl.mem_offset.base = 0;
   tmpl.specific = 1;
-  tmpl.index = 0;
+  tmpl.index = -1;
   tmpl.cmp_type = CMP_NONE;
+  tmpl.depind = 0;
+  tmpl.file = NULL;
+  tmpl.line = 0;
+  tmpl.path = 0;
 
 #define UNHANDLED \
 as_warn (_("Unhandled dependency %s for %s (%s), note %d"), \
@@ -8824,8 +9617,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
              if (idesc->operands[0] == IA64_OPND_AR3
                  && CURR_SLOT.opnd[0].X_add_number - REG_AR == AR_BSPSTORE)
                {
-                 specs[count] = tmpl;
-                 specs[count++].index = 0; /* IA64_RSE_BSPLOAD/RNATBITINDEX */
+                 specs[count++] = tmpl;
                }
            }
          else
@@ -9279,6 +10071,7 @@ note_register_values (idesc)
   else if (idesc->operands[0] == IA64_OPND_R1
           && (idesc->operands[1] == IA64_OPND_IMM22
               || idesc->operands[1] == IA64_OPND_IMMU64)
+          && CURR_SLOT.opnd[1].X_op == O_constant
           && (strcmp (idesc->name, "mov") == 0
               || strcmp (idesc->name, "movl") == 0))
     {
@@ -9296,6 +10089,30 @@ note_register_values (idesc)
            }
        }
     }
+  /* Look for dep.z imm insns.  */
+  else if (idesc->operands[0] == IA64_OPND_R1
+          && idesc->operands[1] == IA64_OPND_IMM8
+          && strcmp (idesc->name, "dep.z") == 0)
+    {
+      int regno = CURR_SLOT.opnd[0].X_add_number - REG_GR;
+      if (regno > 0 && regno < NELEMS (gr_values))
+       {
+         valueT value = CURR_SLOT.opnd[1].X_add_number;
+
+         if (CURR_SLOT.opnd[3].X_add_number < 64)
+           value &= ((valueT)1 << CURR_SLOT.opnd[3].X_add_number) - 1;
+         value <<= CURR_SLOT.opnd[2].X_add_number;
+         gr_values[regno].known = 1;
+         gr_values[regno].value = value;
+         gr_values[regno].path = md.path;
+         if (md.debug_dv)
+           {
+             fprintf (stderr, "  Know gr%d = ", regno);
+             fprintf_vma (stderr, gr_values[regno].value);
+             fputs ("\n", stderr);
+           }
+       }
+    }
   else
     {
       clear_qp_mutex (qp_changemask);
@@ -9401,10 +10218,6 @@ resources_match (rs, idesc, note, qp_regno, path)
       else if (specs[count].index == rs->index)
        return 1;
     }
-#if 0
-  if (md.debug_dv)
-    fprintf (stderr, "  No %s conflicts\n", rs->dependency->name);
-#endif
 
   return 0;
 }
@@ -9520,7 +10333,7 @@ print_dependency (action, depind)
       fprintf (stderr, "  %s %s '%s'",
               action, dv_mode[(regdeps[depind].dependency)->mode],
               (regdeps[depind].dependency)->name);
-      if (regdeps[depind].specific && regdeps[depind].index != 0)
+      if (regdeps[depind].specific && regdeps[depind].index >= 0)
        fprintf (stderr, " (%d)", regdeps[depind].index);
       if (regdeps[depind].mem_offset.hint)
        {
@@ -9587,6 +10400,7 @@ remove_marked_resource (rs)
          struct slot oldslot = CURR_SLOT;
          /* Manually jam a srlz.i insn into the stream */
          memset (&CURR_SLOT, 0, sizeof (CURR_SLOT));
+         CURR_SLOT.user_template = -1;
          CURR_SLOT.idesc = ia64_find_opcode ("srlz.i");
          instruction_serialization ();
          md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
@@ -9608,6 +10422,7 @@ remove_marked_resource (rs)
        struct slot oldslot = CURR_SLOT;
        /* Manually jam a srlz.d insn into the stream */
        memset (&CURR_SLOT, 0, sizeof (CURR_SLOT));
+       CURR_SLOT.user_template = -1;
        CURR_SLOT.idesc = ia64_find_opcode ("srlz.d");
        data_serialization ();
        md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
@@ -9718,7 +10533,7 @@ check_dependencies (idesc)
              if (path != 0)
                sprintf (pathmsg, " when entry is at label '%s'",
                         md.entry_labels[path - 1]);
-             if (rs->specific && rs->index != 0)
+             if (matchtype == 1 && rs->index >= 0)
                sprintf (indexmsg, ", specific resource number is %d",
                         rs->index);
              sprintf (msg, "Use of '%s' %s %s dependency '%s' (%s)%s%s",
@@ -9805,12 +10620,6 @@ mark_resources (idesc)
 
       count = specify_resource (dep, idesc, DV_REG, specs, note, md.path);
 
-#if 0
-      if (md.debug_dv && !count)
-       fprintf (stderr, "  No %s %s usage found (path %d)\n",
-                dv_mode[dep->mode], dep->name, md.path);
-#endif
-
       while (count-- > 0)
        {
          mark_resource (idesc, dep, &specs[count],
@@ -10027,9 +10836,6 @@ md_assemble (str)
            mnemonic = "adds";
          ia64_free_opcode (idesc);
          idesc = ia64_find_opcode (mnemonic);
-#if 0
-         know (!idesc->next);
-#endif
        }
       else if (strcmp (idesc->name, "mov") == 0)
        {
@@ -10048,17 +10854,22 @@ md_assemble (str)
            {
              if (ar_is_only_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
                mnemonic = "mov.i";
-             else
+             else if (ar_is_only_in_memory_unit (CURR_SLOT.opnd[rop].X_add_number))
                mnemonic = "mov.m";
+             else
+               rop = -1;
            }
          else
            abort ();
-         ia64_free_opcode (idesc);
-         idesc = ia64_find_opcode (mnemonic);
-         while (idesc != NULL
-                && (idesc->operands[0] != opnd1
-                    || idesc->operands[1] != opnd2))
-           idesc = get_next_opcode (idesc);
+         if (rop >= 0)
+           {
+             ia64_free_opcode (idesc);
+             idesc = ia64_find_opcode (mnemonic);
+             while (idesc != NULL
+                    && (idesc->operands[0] != opnd1
+                        || idesc->operands[1] != opnd2))
+               idesc = get_next_opcode (idesc);
+           }
        }
     }
   else if (strcmp (idesc->name, "mov.i") == 0
@@ -10083,11 +10894,25 @@ md_assemble (str)
          else if (ar_is_only_in_memory_unit (CURR_SLOT.opnd[rop].X_add_number))
            unit = 'm';
          if (unit != 'a' && unit != idesc->name [4])
-           as_bad ("AR %d cannot be accessed by %c-unit",
+           as_bad ("AR %d can only be accessed by %c-unit",
                    (int) (CURR_SLOT.opnd[rop].X_add_number - REG_AR),
                    TOUPPER (unit));
        }
     }
+  else if (strcmp (idesc->name, "hint.b") == 0)
+    {
+      switch (md.hint_b)
+       {
+       case hint_b_ok:
+         break;
+       case hint_b_warning:
+         as_warn ("hint.b may be treated as nop");
+         break;
+       case hint_b_error:
+         as_bad ("hint.b shouldn't be used");
+         break;
+       }
+    }
 
   qp_regno = 0;
   if (md.qp.X_op == O_register)
@@ -10133,12 +10958,26 @@ md_assemble (str)
   as_where (&CURR_SLOT.src_file, &CURR_SLOT.src_line);
   dwarf2_where (&CURR_SLOT.debug_line);
 
-  /* Add unwind entry, if there is one.  */
+  /* Add unwind entries, if there are any.  */
   if (unwind.current_entry)
     {
       CURR_SLOT.unwind_record = unwind.current_entry;
       unwind.current_entry = NULL;
     }
+  if (unwind.pending_saves)
+    {
+      if (unwind.pending_saves->next)
+       {
+         /* Attach the next pending save to the next slot so that its
+            slot number will get set correctly.  */
+         add_unwind_entry (unwind.pending_saves->next, NOT_A_CHAR);
+         unwind.pending_saves = &unwind.pending_saves->next->r.record.p;
+       }
+      else
+       unwind.pending_saves = NULL;
+    }
+  if (unwind.proc_pending.sym && S_IS_DEFINED (unwind.proc_pending.sym))
+    unwind.insn = 1;
 
   /* Check for dependency violations.  */
   if (md.detect_dv)
@@ -10175,104 +11014,14 @@ void
 md_operand (e)
      expressionS *e;
 {
-  enum pseudo_type pseudo_type;
-  const char *name;
-  size_t len;
-  int ch, i;
-
   switch (*input_line_pointer)
     {
-    case '@':
-      /* Find what relocation pseudo-function we're dealing with.  */
-      pseudo_type = 0;
-      ch = *++input_line_pointer;
-      for (i = 0; i < NELEMS (pseudo_func); ++i)
-       if (pseudo_func[i].name && pseudo_func[i].name[0] == ch)
-         {
-           len = strlen (pseudo_func[i].name);
-           if (strncmp (pseudo_func[i].name + 1,
-                        input_line_pointer + 1, len - 1) == 0
-               && !is_part_of_name (input_line_pointer[len]))
-             {
-               input_line_pointer += len;
-               pseudo_type = pseudo_func[i].type;
-               break;
-             }
-         }
-      switch (pseudo_type)
-       {
-       case PSEUDO_FUNC_RELOC:
-         SKIP_WHITESPACE ();
-         if (*input_line_pointer != '(')
-           {
-             as_bad ("Expected '('");
-             goto err;
-           }
-         /* Skip '('.  */
-         ++input_line_pointer;
-         expression (e);
-         if (*input_line_pointer++ != ')')
-           {
-             as_bad ("Missing ')'");
-             goto err;
-           }
-         if (e->X_op != O_symbol)
-           {
-             if (e->X_op != O_pseudo_fixup)
-               {
-                 as_bad ("Not a symbolic expression");
-                 goto err;
-               }
-             if (i != FUNC_LT_RELATIVE)
-               {
-                 as_bad ("Illegal combination of relocation functions");
-                 goto err;
-               }
-             switch (S_GET_VALUE (e->X_op_symbol))
-               {
-               case FUNC_FPTR_RELATIVE:
-                 i = FUNC_LT_FPTR_RELATIVE; break;
-               case FUNC_DTP_MODULE:
-                 i = FUNC_LT_DTP_MODULE; break;
-               case FUNC_DTP_RELATIVE:
-                 i = FUNC_LT_DTP_RELATIVE; break;
-               case FUNC_TP_RELATIVE:
-                 i = FUNC_LT_TP_RELATIVE; break;
-               default:
-                 as_bad ("Illegal combination of relocation functions");
-                 goto err;
-               }
-           }
-         /* Make sure gas doesn't get rid of local symbols that are used
-            in relocs.  */
-         e->X_op = O_pseudo_fixup;
-         e->X_op_symbol = pseudo_func[i].u.sym;
-         break;
-
-       case PSEUDO_FUNC_CONST:
-         e->X_op = O_constant;
-         e->X_add_number = pseudo_func[i].u.ival;
-         break;
-
-       case PSEUDO_FUNC_REG:
-         e->X_op = O_register;
-         e->X_add_number = pseudo_func[i].u.ival;
-         break;
-
-       default:
-         name = input_line_pointer - 1;
-         get_symbol_end ();
-         as_bad ("Unknown pseudo function `%s'", name);
-         goto err;
-       }
-      break;
-
     case '[':
       ++input_line_pointer;
       expression (e);
       if (*input_line_pointer != ']')
        {
-         as_bad ("Closing bracket misssing");
+         as_bad ("Closing bracket missing");
          goto err;
        }
       else
@@ -10303,7 +11052,7 @@ ia64_fix_adjustable (fix)
      fixS *fix;
 {
   /* Prevent all adjustments to global symbols */
-  if (S_IS_EXTERN (fix->fx_addsy) || S_IS_WEAK (fix->fx_addsy))
+  if (S_IS_EXTERNAL (fix->fx_addsy) || S_IS_WEAK (fix->fx_addsy))
     return 0;
 
   switch (fix->fx_r_type)
@@ -10464,7 +11213,7 @@ ia64_cons_fix_new (f, where, nbytes, exp)
   fix = fix_new_exp (f, where, nbytes, exp, 0, code);
   /* We need to store the byte order in effect in case we're going
      to fix an 8 or 16 bit relocation (for which there no real
-     relocs available).  See md_apply_fix3().  */
+     relocs available).  See md_apply_fix().  */
   fix->tc_fix_data.bigendian = target_big_endian;
 }
 
@@ -10478,6 +11227,7 @@ ia64_gen_real_reloc_type (sym, r_type)
      bfd_reloc_code_real_type r_type;
 {
   bfd_reloc_code_real_type new = 0;
+  const char *type = NULL, *suffix = "";
 
   if (sym == NULL)
     {
@@ -10494,7 +11244,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_FPTR32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_FPTR64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_FPTR64LSB; break;
-       default:                        break;
+       default:                        type = "FPTR"; break;
        }
       break;
 
@@ -10507,7 +11257,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_GPREL32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_GPREL64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_GPREL64LSB; break;
-       default:                        break;
+       default:                        type = "GPREL"; break;
        }
       break;
 
@@ -10516,7 +11266,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        {
        case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_LTOFF22; break;
        case BFD_RELOC_IA64_IMM64:      new = BFD_RELOC_IA64_LTOFF64I; break;
-       default:                        break;
+       default:                        type = "LTOFF"; break;
        }
       break;
 
@@ -10524,7 +11274,7 @@ ia64_gen_real_reloc_type (sym, r_type)
       switch (r_type)
        {
        case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_LTOFF22X; break;
-       default:                        break;
+       default:                        type = "LTOFF"; suffix = "X"; break;
        }
       break;
 
@@ -10537,7 +11287,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_PCREL32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_PCREL64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_PCREL64LSB; break;
-       default:                        break;
+       default:                        type = "PCREL"; break;
        }
       break;
 
@@ -10548,7 +11298,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM64:      new = BFD_RELOC_IA64_PLTOFF64I; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_PLTOFF64MSB;break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_PLTOFF64LSB;break;
-       default:                        break;
+       default:                        type = "PLTOFF"; break;
        }
       break;
 
@@ -10559,7 +11309,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_SECREL32LSB;break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_SECREL64MSB;break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_SECREL64LSB;break;
-       default:                        break;
+       default:                        type = "SECREL"; break;
        }
       break;
 
@@ -10570,7 +11320,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_SEGREL32LSB;break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_SEGREL64MSB;break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_SEGREL64LSB;break;
-       default:                        break;
+       default:                        type = "SEGREL"; break;
        }
       break;
 
@@ -10581,7 +11331,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_LTV32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_LTV64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_LTV64LSB; break;
-       default:                        break;
+       default:                        type = "LTV"; break;
        }
       break;
 
@@ -10592,22 +11342,28 @@ ia64_gen_real_reloc_type (sym, r_type)
          new = BFD_RELOC_IA64_LTOFF_FPTR22; break;
        case BFD_RELOC_IA64_IMM64:
          new = BFD_RELOC_IA64_LTOFF_FPTR64I; break;
+       case BFD_RELOC_IA64_DIR32MSB:
+         new = BFD_RELOC_IA64_LTOFF_FPTR32MSB; break;
+       case BFD_RELOC_IA64_DIR32LSB:
+         new = BFD_RELOC_IA64_LTOFF_FPTR32LSB; break;
+       case BFD_RELOC_IA64_DIR64MSB:
+         new = BFD_RELOC_IA64_LTOFF_FPTR64MSB; break;
+       case BFD_RELOC_IA64_DIR64LSB:
+         new = BFD_RELOC_IA64_LTOFF_FPTR64LSB; break;
        default:
-         break;
+         type = "LTOFF_FPTR"; break;
        }
       break;
 
     case FUNC_TP_RELATIVE:
       switch (r_type)
        {
-       case BFD_RELOC_IA64_IMM14:
-         new = BFD_RELOC_IA64_TPREL14; break;
-       case BFD_RELOC_IA64_IMM22:
-         new = BFD_RELOC_IA64_TPREL22; break;
-       case BFD_RELOC_IA64_IMM64:
-         new = BFD_RELOC_IA64_TPREL64I; break;
-       default:
-         break;
+       case BFD_RELOC_IA64_IMM14:      new = BFD_RELOC_IA64_TPREL14; break;
+       case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_TPREL22; break;
+       case BFD_RELOC_IA64_IMM64:      new = BFD_RELOC_IA64_TPREL64I; break;
+       case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_TPREL64MSB; break;
+       case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_TPREL64LSB; break;
+       default:                        type = "TPREL"; break;
        }
       break;
 
@@ -10617,7 +11373,19 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM22:
          new = BFD_RELOC_IA64_LTOFF_TPREL22; break;
        default:
-         break;
+         type = "LTOFF_TPREL"; break;
+       }
+      break;
+
+    case FUNC_DTP_MODULE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_DIR64MSB:
+         new = BFD_RELOC_IA64_DTPMOD64MSB; break;
+       case BFD_RELOC_IA64_DIR64LSB:
+         new = BFD_RELOC_IA64_DTPMOD64LSB; break;
+       default:
+         type = "DTPMOD"; break;
        }
       break;
 
@@ -10627,13 +11395,17 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM22:
          new = BFD_RELOC_IA64_LTOFF_DTPMOD22; break;
        default:
-         break;
+         type = "LTOFF_DTPMOD"; break;
        }
       break;
 
     case FUNC_DTP_RELATIVE:
       switch (r_type)
        {
+       case BFD_RELOC_IA64_DIR32MSB:
+         new = BFD_RELOC_IA64_DTPREL32MSB; break;
+       case BFD_RELOC_IA64_DIR32LSB:
+         new = BFD_RELOC_IA64_DTPREL32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:
          new = BFD_RELOC_IA64_DTPREL64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:
@@ -10645,7 +11417,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM64:
          new = BFD_RELOC_IA64_DTPREL64I; break;
        default:
-         break;
+         type = "DTPREL"; break;
        }
       break;
 
@@ -10655,22 +11427,49 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM22:
          new = BFD_RELOC_IA64_LTOFF_DTPREL22; break;
        default:
-         break;
+         type = "LTOFF_DTPREL"; break;
        }
       break;
 
     case FUNC_IPLT_RELOC:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IPLTMSB:    return r_type;
+       case BFD_RELOC_IA64_IPLTLSB:    return r_type;
+       default:                        type = "IPLT"; break;
+       }
       break;
 
     default:
       abort ();
     }
 
-  /* Hmmmm.  Should this ever occur?  */
   if (new)
     return new;
   else
-    return r_type;
+    {
+      int width;
+
+      if (!type)
+       abort ();
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_DIR32MSB: width = 32; suffix = "MSB"; break;
+       case BFD_RELOC_IA64_DIR32LSB: width = 32; suffix = "LSB"; break;
+       case BFD_RELOC_IA64_DIR64MSB: width = 64; suffix = "MSB"; break;
+       case BFD_RELOC_IA64_DIR64LSB: width = 64; suffix = "LSB"; break;
+       case BFD_RELOC_UNUSED:        width = 13; break;
+       case BFD_RELOC_IA64_IMM14:    width = 14; break;
+       case BFD_RELOC_IA64_IMM22:    width = 22; break;
+       case BFD_RELOC_IA64_IMM64:    width = 64; suffix = "I"; break;
+       default:                      abort ();
+       }
+
+      /* This should be an error, but since previously there wasn't any
+        diagnostic here, dont't make it fail because of this for now.  */
+      as_warn ("Cannot express %s%d%s relocation", type, width, suffix);
+      return r_type;
+    }
 }
 
 /* Here is where generate the appropriate reloc for pseudo relocation
@@ -10761,7 +11560,7 @@ fix_insn (fix, odesc, value)
    (if possible).  */
 
 void
-md_apply_fix3 (fix, valP, seg)
+md_apply_fix (fix, valP, seg)
      fixS *fix;
      valueT *valP;
      segT seg ATTRIBUTE_UNUSED;
@@ -10773,27 +11572,24 @@ md_apply_fix3 (fix, valP, seg)
 
   if (fix->fx_pcrel)
     {
-      switch (fix->fx_r_type)
-       {
-       case BFD_RELOC_IA64_DIR32MSB:
-         fix->fx_r_type = BFD_RELOC_IA64_PCREL32MSB;
-         break;
-
-       case BFD_RELOC_IA64_DIR32LSB:
-         fix->fx_r_type = BFD_RELOC_IA64_PCREL32LSB;
-         break;
-
-       case BFD_RELOC_IA64_DIR64MSB:
-         fix->fx_r_type = BFD_RELOC_IA64_PCREL64MSB;
-         break;
-
-       case BFD_RELOC_IA64_DIR64LSB:
-         fix->fx_r_type = BFD_RELOC_IA64_PCREL64LSB;
-         break;
-
-       default:
-         break;
-       }
+    switch (fix->fx_r_type)
+      {
+      case BFD_RELOC_IA64_PCREL21B: break;
+      case BFD_RELOC_IA64_PCREL21BI: break;
+      case BFD_RELOC_IA64_PCREL21F: break;
+      case BFD_RELOC_IA64_PCREL21M: break;
+      case BFD_RELOC_IA64_PCREL60B: break;
+      case BFD_RELOC_IA64_PCREL22: break;
+      case BFD_RELOC_IA64_PCREL64I: break;
+      case BFD_RELOC_IA64_PCREL32MSB: break;
+      case BFD_RELOC_IA64_PCREL32LSB: break;
+      case BFD_RELOC_IA64_PCREL64MSB: break;
+      case BFD_RELOC_IA64_PCREL64LSB: break;
+      default:
+       fix->fx_r_type = ia64_gen_real_reloc_type (pseudo_func[FUNC_PC_RELATIVE].u.sym,
+                                              fix->fx_r_type);
+       break;
+      }
     }
   if (fix->fx_addsy)
     {
@@ -10948,14 +11744,6 @@ void
 ia64_handle_align (fragp)
      fragS *fragp;
 {
-  /* Use mfi bundle of nops with no stop bits.  */
-  static const unsigned char le_nop[]
-    = { 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-       0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
-  static const unsigned char le_nop_stop[]
-    = { 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-       0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
-
   int bytes;
   char *p;
   const unsigned char *nop;
@@ -11082,7 +11870,7 @@ dot_alias (int section)
   if (name == end_name)
     {
       as_bad (_("expected symbol name"));
-      discard_rest_of_line ();
+      ignore_rest_of_line ();
       return;
     }
 
@@ -11099,6 +11887,7 @@ dot_alias (int section)
 
   input_line_pointer++;
   *end_name = 0;
+  ia64_canonicalize_symbol_name (name);
 
   /* We call demand_copy_C_string to check if alias string is valid.
      There should be a closing `"' and no `\0' in the string.  */
This page took 0.130175 seconds and 4 git commands to generate.