* read.c (potable): Add string8, string16, string32 and string64. Add bit size for...
[binutils-gdb.git] / gas / config / tc-ia64.c
index d065449638a957061a8e96580dd27f40b3b15ec2..55f05a4b5a1217c1f7cf16778ce66fc8a6162a84 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
 /* 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, 2006, 2007
    Free Software Foundation, Inc.
    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
 
    Free Software Foundation, Inc.
    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
 
@@ -7,7 +7,7 @@
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
    any later version.
 
    GAS is distributed in the hope that it will be useful,
@@ -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
 
    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:
 
 /*
   TODO:
 
 #include "elf/ia64.h"
 
 
 #include "elf/ia64.h"
 
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
 #define NELEMS(a)      ((int) (sizeof (a)/sizeof ((a)[0])))
 #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
 #define MIN(a,b)       ((a) < (b) ? (a) : (b))
 
 #define NUM_SLOTS      4
@@ -117,7 +124,6 @@ enum reg_symbol
     IND_DTR,
     IND_ITR,
     IND_IBR,
     IND_DTR,
     IND_ITR,
     IND_IBR,
-    IND_MEM,
     IND_MSR,
     IND_PKR,
     IND_PMC,
     IND_MSR,
     IND_PKR,
     IND_PMC,
@@ -152,6 +158,7 @@ struct label_fix
 {
   struct label_fix *next;
   struct symbol *sym;
 {
   struct label_fix *next;
   struct symbol *sym;
+  bfd_boolean dw2_mark_labels;
 };
 
 /* This is the endianness of the current section.  */
 };
 
 /* This is the endianness of the current section.  */
@@ -174,6 +181,10 @@ static struct hash_control *alias_name_hash;
 static struct hash_control *secalias_hash;
 static struct hash_control *secalias_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[] = "";
 
 /* Characters which always start a comment.  */
 const char comment_chars[] = "";
 
@@ -182,7 +193,7 @@ const char line_comment_chars[] = "#";
 
 /* Characters which may be used to separate multiple commands on a
    single line.  */
 
 /* 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.  */
 
 /* Characters which are used to indicate an exponent in a floating
    point number.  */
@@ -214,13 +225,26 @@ static struct
     struct hash_control *const_hash;   /* constant hash table */
     struct hash_control *entry_hash;    /* code entry hint hash table */
 
     struct hash_control *const_hash;   /* constant hash table */
     struct hash_control *entry_hash;    /* code entry hint hash table */
 
-    symbolS *regsym[REG_NUM];
-
     /* If X_op is != O_absent, the registername for the instruction's
        qualifying predicate.  If NULL, p0 is assumed for instructions
        that are predicatable.  */
     expressionS qp;
 
     /* If X_op is != O_absent, the registername for the instruction's
        qualifying predicate.  If NULL, p0 is assumed for instructions
        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,
     unsigned int
       manual_bundling : 1,
       debug_dv: 1,
@@ -231,6 +255,13 @@ static struct
       auto_align : 1,
       keep_pending_output : 1;
 
       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.  */
     /* 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.  */
@@ -292,31 +323,29 @@ static struct
                                       the current DV-checking block.  */
     int maxpaths;                   /* size currently allocated for
                                       entry_labels */
                                       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 */
 
     int pointer_size;       /* size in bytes of a pointer */
     int pointer_size_shift; /* shift size of a pointer for alignment */
+
+    symbolS *indregsym[IND_RR - IND_CPUID + 1];
   }
 md;
 
   }
 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
 /* application registers:  */
 
 #define AR_K0          0
@@ -325,33 +354,52 @@ md;
 #define AR_BSP         17
 #define AR_BSPSTORE    18
 #define AR_RNAT                19
 #define AR_BSP         17
 #define AR_BSPSTORE    18
 #define AR_RNAT                19
+#define AR_FCR         21
+#define AR_EFLAG       24
+#define AR_CSD         25
+#define AR_SSD         26
+#define AR_CFLG                27
+#define AR_FSR         28
+#define AR_FIR         29
+#define AR_FDR         30
+#define AR_CCV         32
 #define AR_UNAT                36
 #define AR_FPSR                40
 #define AR_ITC         44
 #define AR_PFS         64
 #define AR_LC          65
 #define AR_UNAT                36
 #define AR_FPSR                40
 #define AR_ITC         44
 #define AR_PFS         64
 #define AR_LC          65
+#define AR_EC          66
 
 static const struct
   {
     const char *name;
 
 static const struct
   {
     const char *name;
-    int regnum;
+    unsigned int regnum;
   }
 ar[] =
   {
   }
 ar[] =
   {
-    {"ar.k0", 0}, {"ar.k1", 1}, {"ar.k2", 2}, {"ar.k3", 3},
-    {"ar.k4", 4}, {"ar.k5", 5}, {"ar.k6", 6}, {"ar.k7", 7},
-    {"ar.rsc",         16}, {"ar.bsp",         17},
-    {"ar.bspstore",    18}, {"ar.rnat",        19},
-    {"ar.fcr",         21}, {"ar.eflag",       24},
-    {"ar.csd",         25}, {"ar.ssd",         26},
-    {"ar.cflg",                27}, {"ar.fsr",         28},
-    {"ar.fir",         29}, {"ar.fdr",         30},
-    {"ar.ccv",         32}, {"ar.unat",        36},
-    {"ar.fpsr",                40}, {"ar.itc",         44},
-    {"ar.pfs",         64}, {"ar.lc",          65},
-    {"ar.ec",          66},
+    {"ar.k0",          AR_K0},         {"ar.k1",       AR_K0 + 1},
+    {"ar.k2",          AR_K0 + 2},     {"ar.k3",       AR_K0 + 3},
+    {"ar.k4",          AR_K0 + 4},     {"ar.k5",       AR_K0 + 5},
+    {"ar.k6",          AR_K0 + 6},     {"ar.k7",       AR_K7},
+    {"ar.rsc",         AR_RSC},        {"ar.bsp",      AR_BSP},
+    {"ar.bspstore",    AR_BSPSTORE},   {"ar.rnat",     AR_RNAT},
+    {"ar.fcr",         AR_FCR},        {"ar.eflag",    AR_EFLAG},
+    {"ar.csd",         AR_CSD},        {"ar.ssd",      AR_SSD},
+    {"ar.cflg",                AR_CFLG},       {"ar.fsr",      AR_FSR},
+    {"ar.fir",         AR_FIR},        {"ar.fdr",      AR_FDR},
+    {"ar.ccv",         AR_CCV},        {"ar.unat",     AR_UNAT},
+    {"ar.fpsr",                AR_FPSR},       {"ar.itc",      AR_ITC},
+    {"ar.pfs",         AR_PFS},        {"ar.lc",       AR_LC},
+    {"ar.ec",          AR_EC},
   };
 
   };
 
+/* control registers:  */
+
+#define CR_DCR           0
+#define CR_ITM           1
+#define CR_IVA           2
+#define CR_PTA           8
+#define CR_GPTA          9
 #define CR_IPSR         16
 #define CR_ISR          17
 #define CR_IIP          19
 #define CR_IPSR         16
 #define CR_ISR          17
 #define CR_IIP          19
@@ -361,49 +409,52 @@ ar[] =
 #define CR_IFS          23
 #define CR_IIM          24
 #define CR_IHA          25
 #define CR_IFS          23
 #define CR_IIM          24
 #define CR_IHA          25
+#define CR_LID          64
 #define CR_IVR          65
 #define CR_TPR          66
 #define CR_EOI          67
 #define CR_IRR0         68
 #define CR_IRR3         71
 #define CR_IVR          65
 #define CR_TPR          66
 #define CR_EOI          67
 #define CR_IRR0         68
 #define CR_IRR3         71
+#define CR_ITV          72
+#define CR_PMV          73
+#define CR_CMCV         74
 #define CR_LRR0         80
 #define CR_LRR1         81
 
 #define CR_LRR0         80
 #define CR_LRR1         81
 
-/* control registers:  */
 static const struct
   {
     const char *name;
 static const struct
   {
     const char *name;
-    int regnum;
+    unsigned int regnum;
   }
 cr[] =
   {
   }
 cr[] =
   {
-    {"cr.dcr",  0},
-    {"cr.itm",  1},
-    {"cr.iva",  2},
-    {"cr.pta",  8},
-    {"cr.gpta",         9},
-    {"cr.ipsr",        16},
-    {"cr.isr", 17},
-    {"cr.iip", 19},
-    {"cr.ifa", 20},
-    {"cr.itir",        21},
-    {"cr.iipa",        22},
-    {"cr.ifs", 23},
-    {"cr.iim", 24},
-    {"cr.iha", 25},
-    {"cr.lid", 64},
-    {"cr.ivr", 65},
-    {"cr.tpr", 66},
-    {"cr.eoi", 67},
-    {"cr.irr0",        68},
-    {"cr.irr1",        69},
-    {"cr.irr2",        70},
-    {"cr.irr3",        71},
-    {"cr.itv", 72},
-    {"cr.pmv", 73},
-    {"cr.cmcv",        74},
-    {"cr.lrr0",        80},
-    {"cr.lrr1",        81}
+    {"cr.dcr", CR_DCR},
+    {"cr.itm", CR_ITM},
+    {"cr.iva", CR_IVA},
+    {"cr.pta", CR_PTA},
+    {"cr.gpta",        CR_GPTA},
+    {"cr.ipsr",        CR_IPSR},
+    {"cr.isr", CR_ISR},
+    {"cr.iip", CR_IIP},
+    {"cr.ifa", CR_IFA},
+    {"cr.itir",        CR_ITIR},
+    {"cr.iipa",        CR_IIPA},
+    {"cr.ifs", CR_IFS},
+    {"cr.iim", CR_IIM},
+    {"cr.iha", CR_IHA},
+    {"cr.lid", CR_LID},
+    {"cr.ivr", CR_IVR},
+    {"cr.tpr", CR_TPR},
+    {"cr.eoi", CR_EOI},
+    {"cr.irr0",        CR_IRR0},
+    {"cr.irr1",        CR_IRR0 + 1},
+    {"cr.irr2",        CR_IRR0 + 2},
+    {"cr.irr3",        CR_IRR3},
+    {"cr.itv", CR_ITV},
+    {"cr.pmv", CR_PMV},
+    {"cr.cmcv",        CR_CMCV},
+    {"cr.lrr0",        CR_LRR0},
+    {"cr.lrr1",        CR_LRR1}
   };
 
 #define PSR_MFL         4
   };
 
 #define PSR_MFL         4
@@ -461,7 +512,7 @@ const_bits[] =
 static const struct
   {
     const char *name;
 static const struct
   {
     const char *name;
-    int regnum;
+    unsigned int regnum;
   }
 indirect_reg[] =
   {
   }
 indirect_reg[] =
   {
@@ -514,10 +565,10 @@ pseudo_func[] =
     { "segrel",        PSEUDO_FUNC_RELOC, { 0 } },
     { "tprel", PSEUDO_FUNC_RELOC, { 0 } },
     { "ltv",   PSEUDO_FUNC_RELOC, { 0 } },
     { "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:  */
     { "iplt",  PSEUDO_FUNC_RELOC, { 0 } },
 
     /* mbtype4 constants:  */
@@ -564,7 +615,7 @@ static const bfd_vma nop[IA64_NUM_UNITS] =
     0x0008000000LL,    /* M-unit nop */
     0x4000000000LL,    /* B-unit nop */
     0x0008000000LL,    /* F-unit nop */
     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 */
   };
 
     0x0008000000LL,    /* X-unit nop */
   };
 
@@ -635,7 +686,17 @@ static struct gr {
   unsigned known:1;
   int path;
   valueT value;
   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;
 
 /* Remember the alignment frag.  */
 static fragS *align_frag;
@@ -656,8 +717,6 @@ typedef struct unw_rec_list {
   unwind_record r;
   unsigned long slot_number;
   fragS *slot_frag;
   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;
 
   struct unw_rec_list *next;
 } unw_rec_list;
 
@@ -672,6 +731,12 @@ typedef struct label_prologue_count
   unsigned int prologue_count;
 } 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.  */
 static struct
 {
   /* Maintain a list of unwind entries for the current function.  */
@@ -683,8 +748,7 @@ static struct
   unw_rec_list *current_entry;
 
   /* These are used to create the unwind table entry for this function.  */
   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;
   symbolS *info;               /* pointer to unwind info */
   symbolS *personality_routine;
   segT saved_text_seg;
@@ -692,11 +756,17 @@ static struct
   unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */
 
   /* TRUE if processing unwind directives in a prologue region.  */
   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;
   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
 } unwind;
 
 /* The input value is a negated offset from psp, and specifies an address
@@ -718,11 +788,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_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_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));
 static void dot_handlerdata  PARAMS ((int));
 static void dot_unwentry PARAMS ((int));
 static void dot_altrp PARAMS ((int));
@@ -734,8 +802,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_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));
 static void dot_label_state PARAMS ((int));
 static void dot_copy_state PARAMS ((int));
 static void dot_unwabi PARAMS ((int));
@@ -750,7 +816,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 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));
 static void dot_xdata PARAMS ((int));
 static void stmt_float_cons PARAMS ((int));
 static void stmt_cons_ua PARAMS ((int));
@@ -765,16 +831,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 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 symbolS *declare_register PARAMS ((const char *name, int regnum));
-static void declare_register_set PARAMS ((const char *, int, int));
+static void add_unwind_entry PARAMS((unw_rec_list *, int));
+static symbolS *declare_register PARAMS ((const char *name, unsigned int regnum));
+static void declare_register_set PARAMS ((const char *, unsigned int, unsigned 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 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 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));
 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));
@@ -894,15 +959,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_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));
                                                    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 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));
                                                 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));
@@ -913,8 +974,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));
                                         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));
 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));
@@ -1043,6 +1105,7 @@ ia64_flush_insns ()
   segT saved_seg;
   subsegT saved_subseg;
   unw_rec_list *ptr;
   segT saved_seg;
   subsegT saved_subseg;
   unw_rec_list *ptr;
+  bfd_boolean mark;
 
   if (!md.last_text_seg)
     return;
 
   if (!md.last_text_seg)
     return;
@@ -1056,18 +1119,23 @@ ia64_flush_insns ()
     emit_one_bundle ();                /* force out queued instructions */
 
   /* In case there are labels following the last instruction, resolve
     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)
     {
   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,
   CURR_SLOT.tag_fixups = 0;
 
   /* In case there are unwind directives following the last instruction,
@@ -1138,7 +1206,7 @@ ia64_cons_align (nbytes)
 }
 
 /* Output COUNT bytes to a memory location.  */
 }
 
 /* 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)
 
 void
 output_vbyte_mem (count, ptr, comment)
@@ -1315,7 +1383,7 @@ output_P4_format (f, imask, imask_size)
      unsigned long imask_size;
 {
   imask[0] = UNW_P4;
      unsigned long imask_size;
 {
   imask[0] = UNW_P4;
-  (*f) (imask_size, imask, NULL);
+  (*f) (imask_size, (char *) imask, NULL);
 }
 
 static void
 }
 
 static void
@@ -1701,6 +1769,55 @@ output_X4_format (f, qp, ab, reg, x, y, treg, t)
   (*f) (count, bytes, NULL);
 }
 
   (*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 *
 /* This function allocates a record list structure, and initializes fields.  */
 
 static unw_rec_list *
@@ -1708,11 +1825,9 @@ alloc_record (unw_record_type t)
 {
   unw_rec_list *ptr;
   ptr = xmalloc (sizeof (*ptr));
 {
   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->slot_number = SLOT_NUM_NOT_SET;
   ptr->r.type = t;
-  ptr->next_slot_number = 0;
-  ptr->next_slot_frag = 0;
   return ptr;
 }
 
   return ptr;
 }
 
@@ -1774,7 +1889,7 @@ output_psp_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (psp_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;
 }
 
   return ptr;
 }
 
@@ -1783,7 +1898,7 @@ output_psp_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (psp_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -1799,7 +1914,7 @@ output_rp_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (rp_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;
 }
 
   return ptr;
 }
 
@@ -1808,7 +1923,7 @@ output_rp_br (br)
      unsigned int br;
 {
   unw_rec_list *ptr = alloc_record (rp_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;
 }
 
   return ptr;
 }
 
@@ -1817,7 +1932,7 @@ output_rp_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rp_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rp_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -1826,7 +1941,7 @@ output_rp_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rp_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -1842,7 +1957,7 @@ output_pfs_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (pfs_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;
 }
 
   return ptr;
 }
 
@@ -1851,7 +1966,7 @@ output_pfs_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (pfs_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (pfs_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -1860,7 +1975,7 @@ output_pfs_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (pfs_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -1876,7 +1991,7 @@ output_preds_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (preds_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;
 }
 
   return ptr;
 }
 
@@ -1885,7 +2000,7 @@ output_preds_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (preds_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (preds_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -1894,7 +2009,7 @@ output_preds_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (preds_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -1903,8 +2018,24 @@ output_fr_mem (mask)
      unsigned int mask;
 {
   unw_rec_list *ptr = alloc_record (fr_mem);
      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 *
 }
 
 static unw_rec_list *
@@ -1913,9 +2044,39 @@ output_frgr_mem (gr_mask, fr_mask)
      unsigned int fr_mask;
 {
   unw_rec_list *ptr = alloc_record (frgr_mem);
      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 *
 }
 
 static unw_rec_list *
@@ -1924,9 +2085,27 @@ output_gr_gr (mask, reg)
      unsigned int reg;
 {
   unw_rec_list *ptr = alloc_record (gr_gr);
      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.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 *
 }
 
 static unw_rec_list *
@@ -1934,27 +2113,77 @@ output_gr_mem (mask)
      unsigned int mask;
 {
   unw_rec_list *ptr = alloc_record (gr_mem);
      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);
 }
 
 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;
   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 *
 }
 
 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);
      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 *
 }
 
 static unw_rec_list *
@@ -1962,7 +2191,7 @@ output_spill_base (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (spill_base);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (spill_base);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -1978,7 +2207,7 @@ output_unat_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (unat_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;
 }
 
   return ptr;
 }
 
@@ -1987,7 +2216,7 @@ output_unat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (unat_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (unat_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -1996,7 +2225,7 @@ output_unat_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (unat_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -2012,7 +2241,7 @@ output_lc_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (lc_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;
 }
 
   return ptr;
 }
 
@@ -2021,7 +2250,7 @@ output_lc_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (lc_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (lc_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -2030,7 +2259,7 @@ output_lc_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (lc_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -2046,7 +2275,7 @@ output_fpsr_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (fpsr_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;
 }
 
   return ptr;
 }
 
@@ -2055,7 +2284,7 @@ output_fpsr_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (fpsr_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (fpsr_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -2064,7 +2293,7 @@ output_fpsr_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (fpsr_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -2087,7 +2316,7 @@ output_priunat_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (priunat_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;
 }
 
   return ptr;
 }
 
@@ -2096,7 +2325,7 @@ output_priunat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (priunat_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (priunat_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -2105,7 +2334,7 @@ output_priunat_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (priunat_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -2121,7 +2350,7 @@ output_bsp_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (bsp_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;
 }
 
   return ptr;
 }
 
@@ -2130,7 +2359,7 @@ output_bsp_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bsp_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bsp_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -2139,7 +2368,7 @@ output_bsp_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bsp_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -2155,7 +2384,7 @@ output_bspstore_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (bspstore_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;
 }
 
   return ptr;
 }
 
@@ -2164,7 +2393,7 @@ output_bspstore_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bspstore_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bspstore_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -2173,7 +2402,7 @@ output_bspstore_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bspstore_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -2189,7 +2418,7 @@ output_rnat_gr (gr)
      unsigned int gr;
 {
   unw_rec_list *ptr = alloc_record (rnat_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;
 }
 
   return ptr;
 }
 
@@ -2198,7 +2427,7 @@ output_rnat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rnat_psprel);
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rnat_psprel);
-  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
   return ptr;
 }
 
@@ -2207,7 +2436,7 @@ output_rnat_sprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rnat_sprel);
      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;
 }
 
   return ptr;
 }
 
@@ -2247,88 +2476,47 @@ output_copy_state (unsigned long label)
 }
 
 static unw_rec_list *
 }
 
 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 = ENCODED_PSP_OFFSET (offset);
-  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;
 {
      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.ab = ab;
   ptr->r.record.x.reg = reg;
-  ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
+  ptr->r.record.x.where.pspoff = ENCODED_PSP_OFFSET (offset);
   ptr->r.record.x.qp = predicate;
   return ptr;
 }
 
 static unw_rec_list *
   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;
 {
      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.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 *
   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;
 {
      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.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;
   ptr->r.record.x.xy = xy;
   ptr->r.record.x.qp = predicate;
   return ptr;
@@ -2342,7 +2530,7 @@ process_one_record (ptr, f)
      unw_rec_list *ptr;
      vbyte_func f;
 {
      unw_rec_list *ptr;
      vbyte_func f;
 {
-  unsigned long fr_mask, gr_mask;
+  unsigned int fr_mask, gr_mask;
 
   switch (ptr->r.type)
     {
 
   switch (ptr->r.type)
     {
@@ -2408,13 +2596,13 @@ process_one_record (ptr, f)
     case bsp_gr:
     case bspstore_gr:
     case rnat_gr:
     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:
       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:
       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:
       break;
     case rp_when:
     case pfs_when:
@@ -2431,7 +2619,7 @@ process_one_record (ptr, f)
     case lc_psprel:
     case fpsr_psprel:
     case spill_base:
     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:
       break;
     case rp_sprel:
     case pfs_sprel:
@@ -2443,13 +2631,29 @@ process_one_record (ptr, f)
     case bsp_sprel:
     case bspstore_sprel:
     case rnat_sprel:
     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:
       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:
       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.");
       break;
     case spill_mask:
       as_bad ("spill_mask record unimplemented.");
@@ -2465,7 +2669,7 @@ process_one_record (ptr, f)
     case bsp_psprel:
     case bspstore_psprel:
     case rnat_psprel:
     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);
       break;
     case unwabi:
       output_P10_format (f, ptr->r.record.p.abi, ptr->r.record.p.context);
@@ -2480,32 +2684,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,
     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,
       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,
       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,
       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,
       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,
       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:
                        ptr->r.record.x.t);
       break;
     default:
@@ -2536,6 +2740,28 @@ calc_record_size (list)
   return vbyte_count;
 }
 
   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
 /* 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
@@ -2667,6 +2893,13 @@ 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;
       /* 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.
+        It is too difficult to recover safely from this problem, so we just
+        exit with an error.  */
+      if (first_frag == NULL)
+       as_fatal ("Section switching in code is not supported.");
     }
 
   /* Add in the used part of the last frag.  */
     }
 
   /* Add in the used part of the last frag.  */
@@ -2785,7 +3018,7 @@ fixup_unw_records (list, before_relax)
        case frgr_mem:
          if (!region)
            {
        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;
              return;
            }
          region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask;
@@ -2796,25 +3029,25 @@ fixup_unw_records (list, before_relax)
        case fr_mem:
          if (!region)
            {
        case fr_mem:
          if (!region)
            {
-             as_bad ("fr_mem record before region record!\n");
+             as_bad ("fr_mem record before region record!");
              return;
            }
              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)
            {
          break;
        case gr_mem:
          if (!region)
            {
-             as_bad ("gr_mem record before region record!\n");
+             as_bad ("gr_mem record before region record!");
              return;
            }
              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)
            {
          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;
              return;
            }
          region->r.record.r.mask.br_mem |= ptr->r.record.p.brmask;
@@ -2824,7 +3057,7 @@ fixup_unw_records (list, before_relax)
        case gr_gr:
          if (!region)
            {
        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);
              return;
            }
          set_imask (region, ptr->r.record.p.grmask, t, 2);
@@ -2832,7 +3065,7 @@ fixup_unw_records (list, before_relax)
        case br_gr:
          if (!region)
            {
        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);
              return;
            }
          set_imask (region, ptr->r.record.p.brmask, t, 3);
@@ -2863,8 +3096,11 @@ ia64_estimate_size_before_relax (fragS *frag,
   pad = len % md.pointer_size;
   if (pad != 0)
     len += md.pointer_size - pad;
   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.  */
 
   /* fr_var carries the max_chars that we created the fragment with.
      We must, of course, have allocated enough memory earlier.  */
@@ -2891,8 +3127,11 @@ ia64_convert_frag (fragS *frag)
   pad = len % md.pointer_size;
   if (pad != 0)
     len += md.pointer_size - pad;
   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.  */
 
   /* fr_var carries the max_chars that we created the fragment with.
      We must, of course, have allocated enough memory earlier.  */
@@ -2933,17 +3172,43 @@ ia64_convert_frag (fragS *frag)
 }
 
 static int
 }
 
 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;
      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)
 
   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;
   if (reg >= (REG_GR + 4) && reg <= (REG_GR + 7))
     {
       *ab = 0;
@@ -2978,31 +3243,33 @@ convert_expr_to_ab_reg (e, ab, regp)
        case REG_AR + AR_LC:    *regp = 10; break;
 
        default:
        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;
      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;
     }
     {
       *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;
     {
       *xy = 1;
       *regp = reg - REG_FR;
@@ -3013,8 +3280,7 @@ convert_expr_to_xy_reg (e, xy, regp)
       *regp = reg - REG_BR;
     }
   else
       *regp = reg - REG_BR;
     }
   else
-    return -1;
-  return 1;
+    as_bad ("Operand %d to .%s must be a writable register", n, po);
 }
 
 static void
 }
 
 static void
@@ -3029,17 +3295,20 @@ static void
 dot_radix (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
 dot_radix (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  int radix;
+  char *radix;
+  int ch;
 
   SKIP_WHITESPACE ();
 
   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
 }
 
 /* Helper function for .loc directives.  If the assembler is not generating
@@ -3061,19 +3330,112 @@ dot_special_section (which)
   set_section ((char *) special_section_name[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
 static void
-add_unwind_entry (ptr)
+add_unwind_entry (ptr, sep)
      unw_rec_list *ptr;
      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;
 
   /* 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
 }
 
 static void
@@ -3081,13 +3443,19 @@ dot_fframe (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
      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)
 
   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
 }
 
 static void
@@ -3096,49 +3464,47 @@ dot_vframe (dummy)
 {
   expressionS e;
   unsigned reg;
 {
   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;
   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
 }
 
 static void
-dot_vframesp (dummy)
-     int dummy ATTRIBUTE_UNUSED;
+dot_vframesp (psp)
+     int psp;
 {
   expressionS e;
 {
   expressionS e;
+  int 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));
-    }
-  else
-    as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
-}
+  if (psp)
+    as_warn (".vframepsp is meaningless, assuming .vframesp was meant");
 
 
-static void
-dot_vframepsp (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  expressionS e;
+  if (!in_prologue ("vframesp"))
+    return;
 
 
-  parse_operand (&e);
-  if (e.X_op == O_constant)
+  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 .vframepsp must be a constant (psp-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
 }
 
 static void
@@ -3146,100 +3512,115 @@ dot_save (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
+  unsigned reg1, reg2;
   int sep;
   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'.  */
   /* 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;
 {
 }
 
 static void
 dot_restore (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
+  expressionS e1;
   unsigned long ecount;        /* # of _additional_ regions to pop */
   int sep;
 
   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)
   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 == ',')
     {
 
   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");
       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;
     }
        }
       ecount = e2.X_add_number;
     }
@@ -3250,10 +3631,10 @@ dot_restore (dummy)
     {
       as_bad ("Epilogue count of %lu exceeds number of nested prologues (%u)",
              ecount + 1, unwind.prologue_count);
     {
       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;
 
   if (ecount < unwind.prologue_count)
     unwind.prologue_count -= ecount + 1;
@@ -3262,52 +3643,27 @@ dot_restore (dummy)
 }
 
 static void
 }
 
 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;
 {
   unsigned int qp, ab, reg;
-  expressionS e1, e2;
+  expressionS e;
   int sep;
   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[] =
 }
 
 static char *special_linkonce_name[] =
@@ -3316,7 +3672,7 @@ static char *special_linkonce_name[] =
   };
 
 static void
   };
 
 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
 {
   /*
     Use a slightly ugly scheme to derive the unwind section names from
@@ -3378,8 +3734,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;
     }
       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);
 
   prefix_len = strlen (prefix);
   suffix_len = strlen (suffix);
@@ -3390,7 +3744,8 @@ start_unwind_section (const segT text_seg, int sec_index, int linkonce_empty)
   sec_name [sec_name_len] = '\0';
 
   /* Handle COMDAT group.  */
   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;
     {
       char *section;
       size_t len, group_name_len;
@@ -3436,7 +3791,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.  */
 
   /* 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.  */
 
   /* Force out pending instructions, to make sure all unwind records have
      a valid slot_number field.  */
@@ -3454,9 +3809,11 @@ generate_unwind_image (const segT text_seg)
       pad = size % md.pointer_size;
       if (pad != 0)
        size += md.pointer_size - pad;
       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.  */
     }
 
   /* If there are unwind records, switch sections, and output the info.  */
@@ -3465,7 +3822,7 @@ generate_unwind_image (const segT text_seg)
       expressionS exp;
       bfd_reloc_code_real_type reloc;
 
       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.  */
 
       /* Make sure the section has 4 byte alignment for ILP32 and
         8 byte alignment for LP64.  */
@@ -3506,8 +3863,6 @@ generate_unwind_image (const segT text_seg)
          unwind.personality_routine = 0;
        }
     }
          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;
 
   free_saved_prologue_counts ();
   unwind.list = unwind.tail = unwind.current_entry = NULL;
@@ -3517,6 +3872,8 @@ static void
 dot_handlerdata (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
 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 */
   unwind.force_unwind_entry = 1;
 
   /* Remember which segment we're in so we can switch back after .endp */
@@ -3534,6 +3891,8 @@ static void
 dot_unwentry (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
 dot_unwentry (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("unwentry"))
+    return;
   unwind.force_unwind_entry = 1;
   demand_empty_rest_of_line ();
 }
   unwind.force_unwind_entry = 1;
   demand_empty_rest_of_line ();
 }
@@ -3545,12 +3904,17 @@ dot_altrp (dummy)
   expressionS e;
   unsigned reg;
 
   expressionS e;
   unsigned reg;
 
-  parse_operand (&e);
+  if (!in_prologue ("altrp"))
+    return;
+
+  parse_operand (&e, 0);
   reg = e.X_add_number - REG_BR;
   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
 }
 
 static void
@@ -3560,371 +3924,343 @@ dot_savemem (psprel)
   expressionS e1, e2;
   int sep;
   int reg1, val;
   expressionS e1, e2;
   int sep;
   int reg1, val;
+  const char * const po = psprel ? "savepsp" : "savesp";
+
+  if (!in_prologue (po))
+    return;
 
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    as_bad ("No second operand to .save%ssp", psprel ? "p" : "");
-  sep = parse_operand (&e2);
+  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'.  */
 
   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
     }
 }
 
 static void
-dot_savef (dummy)
+dot_saveg (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1;
+  expressionS e;
+  unsigned grmask;
   int sep;
   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 == ',')
     {
 
   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;
        }
        }
-      add_unwind_entry (output_br_gr (brmask, e2.X_add_number));
+      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_gr_gr (grmask, reg), 0);
     }
   else
     }
   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
 }
 
 static void
-dot_savegf (dummy)
+dot_savef (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
      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
 }
 
 static void
-dot_spill (dummy)
+dot_saveb (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
      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
 }
 
 static void
-dot_spillmem (psprel)
-     int psprel;
+dot_savegf (dummy)
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
 {
   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
 }
 
 static void
-dot_spillreg_p (dummy)
+dot_spill (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
      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
 }
 
 static void
-dot_spillmem_p (psprel)
+dot_spillmem (psprel)
      int 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);
-
-  qp = e1.X_add_number - REG_P;
-  if (e1.X_op != O_register || qp > 63)
-    {
-      as_bad ("First operand to .spill%s_p must be a predicate",
-             psprel ? "psp" : "sp");
-      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 .spill%s_p must be a preserved register",
-             psprel ? "psp" : "sp");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
 
-  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)
     }
 
   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
   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
 }
 
 static unsigned int
@@ -3988,14 +4324,18 @@ dot_label_state (dummy)
 {
   expressionS e;
 
 {
   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");
     {
       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
 }
 
 static void
@@ -4004,14 +4344,18 @@ dot_copy_state (dummy)
 {
   expressionS e;
 
 {
   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");
     {
       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
 }
 
 static void
@@ -4021,29 +4365,28 @@ dot_unwabi (dummy)
   expressionS e1, e2;
   unsigned char sep;
 
   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");
 
   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");
     }
 
   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
 }
 
 static void
@@ -4051,6 +4394,8 @@ dot_personality (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   char *name, *p, c;
      int dummy ATTRIBUTE_UNUSED;
 {
   char *name, *p, c;
+  if (!in_procedure ("personality"))
+    return;
   SKIP_WHITESPACE ();
   name = input_line_pointer;
   c = get_symbol_end ();
   SKIP_WHITESPACE ();
   name = input_line_pointer;
   c = get_symbol_end ();
@@ -4068,8 +4413,22 @@ dot_proc (dummy)
 {
   char *name, *p, c;
   symbolS *sym;
 {
   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)
   /* Parse names of main and alternate entry points and mark them as
      function symbols:  */
   while (1)
@@ -4078,22 +4437,45 @@ dot_proc (dummy)
       name = input_line_pointer;
       c = get_symbol_end ();
       p = input_line_pointer;
       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;
     }
       *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);
 
   demand_empty_rest_of_line ();
   ia64_do_align (16);
 
+  unwind.prologue = 0;
   unwind.prologue_count = 0;
   unwind.prologue_count = 0;
+  unwind.body = 0;
+  unwind.insn = 0;
   unwind.list = unwind.tail = unwind.current_entry = NULL;
   unwind.personality_routine = 0;
 }
   unwind.list = unwind.tail = unwind.current_entry = NULL;
   unwind.personality_routine = 0;
 }
@@ -4102,52 +4484,86 @@ static void
 dot_body (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
 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.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;
 {
 }
 
 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
       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
   else
-    add_unwind_entry (output_prologue ());
+    add_unwind_entry (output_prologue (), 0);
 
   unwind.prologue = 1;
   unwind.prologue_mask = mask;
 
   unwind.prologue = 1;
   unwind.prologue_mask = mask;
+  unwind.prologue_gr = grsave;
+  unwind.body = 0;
   ++unwind.prologue_count;
 }
 
   ++unwind.prologue_count;
 }
 
@@ -4156,13 +4572,17 @@ dot_endp (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
-  unsigned char *ptr;
   int bytes_per_address;
   long where;
   segT saved_seg;
   subsegT saved_subseg;
   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)
     {
 
   if (unwind.saved_text_seg)
     {
@@ -4184,10 +4604,12 @@ dot_endp (dummy)
 
   if (unwind.info || unwind.force_unwind_entry)
     {
 
   if (unwind.info || unwind.force_unwind_entry)
     {
+      symbolS *proc_end;
+
       subseg_set (md.last_text_seg, 0);
       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.  */
 
       /* Make sure that section has 4 byte alignment for ILP32 and
          8 byte alignment for LP64.  */
@@ -4195,7 +4617,7 @@ dot_endp (dummy)
 
       /* Need space for 3 pointers for procedure start, procedure end,
         and unwind info.  */
 
       /* 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;
 
       where = frag_now_fix () - (3 * md.pointer_size);
       bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8;
 
@@ -4203,13 +4625,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_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;
       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);
 
       ia64_cons_fix_new (frag_now, where + bytes_per_address,
                         bytes_per_address, &e);
 
@@ -4222,51 +4650,71 @@ dot_endp (dummy)
          ia64_cons_fix_new (frag_now, where + (bytes_per_address * 2),
                             bytes_per_address, &e);
        }
          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);
 
   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)
     {
   while (1)
     {
+      char *name, *p, c;
+
       SKIP_WHITESPACE ();
       name = input_line_pointer;
       c = get_symbol_end ();
       p = input_line_pointer;
       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 ();
        }
       *p = c;
       SKIP_WHITESPACE ();
@@ -4275,7 +4723,21 @@ dot_endp (dummy)
       ++input_line_pointer;
     }
   demand_empty_rest_of_line ();
       ++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
 }
 
 static void
@@ -4318,7 +4780,8 @@ static void
 dot_rot (type)
      int type;
 {
 dot_rot (type)
      int type;
 {
-  unsigned num_regs, num_alloced = 0;
+  offsetT num_regs;
+  valueT num_alloced = 0;
   struct dynreg **drpp, *dr;
   int ch, base_reg = 0;
   char *name, *start;
   struct dynreg **drpp, *dr;
   int ch, base_reg = 0;
   char *name, *start;
@@ -4336,6 +4799,7 @@ dot_rot (type)
   for (dr = md.dynreg[type]; dr && dr->num_regs; dr = dr->next)
     {
       hash_delete (md.dynreg_hash, dr->name);
   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;
     }
 
       dr->num_regs = 0;
     }
 
@@ -4344,8 +4808,8 @@ dot_rot (type)
     {
       start = input_line_pointer;
       ch = get_symbol_end ();
     {
       start = input_line_pointer;
       ch = get_symbol_end ();
+      len = strlen (ia64_canonicalize_symbol_name (start));
       *input_line_pointer = ch;
       *input_line_pointer = ch;
-      len = (input_line_pointer - start);
 
       SKIP_WHITESPACE ();
       if (*input_line_pointer != '[')
 
       SKIP_WHITESPACE ();
       if (*input_line_pointer != '[')
@@ -4362,6 +4826,11 @@ dot_rot (type)
          as_bad ("Expected ']'");
          goto err;
        }
          as_bad ("Expected ']'");
          goto err;
        }
+      if (num_regs <= 0)
+       {
+         as_bad ("Number of elements must be positive");
+         goto err;
+       }
       SKIP_WHITESPACE ();
 
       num_alloced += num_regs;
       SKIP_WHITESPACE ();
 
       num_alloced += num_regs;
@@ -4394,16 +4863,16 @@ dot_rot (type)
          break;
        }
 
          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));
        }
 
       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;
       dr = *drpp;
       dr->name = name;
       dr->num_regs = num_regs;
@@ -4414,6 +4883,7 @@ dot_rot (type)
       if (hash_insert (md.dynreg_hash, name, dr))
        {
          as_bad ("Attempt to redefine register set `%s'", name);
       if (hash_insert (md.dynreg_hash, name, dr))
        {
          as_bad ("Attempt to redefine register set `%s'", name);
+         obstack_free (&notes, name);
          goto err;
        }
 
          goto err;
        }
 
@@ -4501,49 +4971,75 @@ dot_ln (dummy)
   demand_empty_rest_of_line ();
 }
 
   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 ();
 
   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 ();
   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;
 {
 }
 
 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?  */
 }
 
 /* Why doesn't float_cons() call md_cons_align() the way cons() does?  */
@@ -4589,66 +5085,27 @@ static void
 dot_xfloat_cons (kind)
      int kind;
 {
 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
 }
 
 static void
-dot_xstringer (zero)
-     int zero;
+dot_xstringer (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;
 {
 }
 
 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;
 {
 }
 
 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 */
 }
 
 /* .reg.val <regname>,value */
@@ -4659,7 +5116,7 @@ dot_reg_val (dummy)
 {
   expressionS reg;
 
 {
   expressionS reg;
 
-  expression (&reg);
+  expression_and_evaluate (&reg);
   if (reg.X_op != O_register)
     {
       as_bad (_("Register name expected"));
   if (reg.X_op != O_register)
     {
       as_bad (_("Register name expected"));
@@ -4674,7 +5131,7 @@ dot_reg_val (dummy)
     {
       valueT value = get_absolute_expression ();
       int regno = reg.X_add_number;
     {
       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
        {
        as_warn (_("Register value annotation ignored"));
       else
        {
@@ -4765,9 +5222,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 [,...]]
  */
 
   .pred.safe_across_calls p1 [, p2 [,...]]
  */
 
@@ -4781,94 +5238,104 @@ dot_pred_rel (type)
 
   if (type == 0)
     {
 
   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);
        {
          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';
          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);
        }
        }
-      if (*input_line_pointer == ',')
-       ++input_line_pointer;
-      SKIP_WHITESPACE ();
-    }
-
-  SKIP_WHITESPACE ();
-  while (1)
-    {
-      valueT bit = 1;
-      int regno;
+      else if (*input_line_pointer == '@')
+       {
+         char *form = ++input_line_pointer;
+         char c = get_symbol_end();
 
 
-      if (TOUPPER (*input_line_pointer) != 'P'
-         || (regno = atoi (++input_line_pointer)) < 0
-         || regno > 63)
+         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 (_("Predicate register expected"));
+         as_bad (_("Missing predicate relation type"));
          ignore_rest_of_line ();
          return;
        }
          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 == '-')
+      if (type == 0)
        {
        {
-         valueT stop = 1;
-         ++input_line_pointer;
+         as_bad (_("Unrecognized predicate relation type"));
+         ignore_rest_of_line ();
+         return;
+       }
+      if (*input_line_pointer == ',')
+       ++input_line_pointer;
+      SKIP_WHITESPACE ();
+    }
 
 
-         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)
+  while (1)
+    {
+      valueT bits = 1;
+      int sep, regno;
+      expressionS pr, *pr1, *pr2;
+
+      sep = parse_operand (&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;
            }
            {
              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;
        }
        }
-      if (*input_line_pointer != ',')
+      else
+       {
+         as_bad (_("Predicate register expected"));
+         ignore_rest_of_line ();
+         return;
+       }
+      if (mask & bits)
+       as_warn (_("Duplicate predicate register ignored"));
+      mask |= bits;
+      if (sep != ',')
        break;
        break;
-      ++input_line_pointer;
-      SKIP_WHITESPACE ();
     }
 
   switch (type)
     }
 
   switch (type)
@@ -5002,11 +5469,11 @@ const pseudo_typeS md_pseudo_table[] =
     { "fframe", dot_fframe, 0 },
     { "vframe", dot_vframe, 0 },
     { "vframesp", dot_vframesp, 0 },
     { "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 },
     { "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 },
     { "handlerdata", dot_handlerdata, 0 },
     { "unwentry", dot_unwentry, 0 },
     { "altrp", dot_altrp, 0 },
@@ -5020,16 +5487,13 @@ const pseudo_typeS md_pseudo_table[] =
     { "spillreg", dot_spillreg, 0 },
     { "spillsp", dot_spillmem, 0 },
     { "spillpsp", dot_spillmem, 1 },
     { "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 },
     { "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 },
     { "mii", dot_template, 0x0 },
     { "mli", dot_template, 0x2 }, /* old format, for compatibility */
     { "mlx", dot_template, 0x2 },
@@ -5041,10 +5505,6 @@ const pseudo_typeS md_pseudo_table[] =
     { "bbb", dot_template, 0xb },
     { "mmb", dot_template, 0xc },
     { "mfb", dot_template, 0xe },
     { "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 },
     { "align", dot_align, 0 },
     { "regstk", dot_regstk, 0 },
     { "rotr", dot_rot, DYNREG_GR },
@@ -5061,17 +5521,19 @@ const pseudo_typeS md_pseudo_table[] =
     { "xdata2", dot_xdata, 2 },
     { "xdata4", dot_xdata, 4 },
     { "xdata8", dot_xdata, 8 },
     { "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' },
     { "xreal16", dot_xfloat_cons, 'X' },
     { "xreal4", dot_xfloat_cons, 'f' },
     { "xreal8", dot_xfloat_cons, 'd' },
     { "xreal10", dot_xfloat_cons, 'x' },
     { "xreal16", dot_xfloat_cons, 'X' },
-    { "xstring", dot_xstringer, 0 },
-    { "xstringz", dot_xstringer, 1 },
+    { "xstring", dot_xstringer, 8 + 0 },
+    { "xstringz", dot_xstringer, 8 + 1 },
 
     /* unaligned versions:  */
     { "xdata2.ua", dot_xdata_ua, 2 },
     { "xdata4.ua", dot_xdata_ua, 4 },
     { "xdata8.ua", dot_xdata_ua, 8 },
 
     /* unaligned versions:  */
     { "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' },
     { "xreal4.ua", dot_xfloat_cons_ua, 'f' },
     { "xreal8.ua", dot_xfloat_cons_ua, 'd' },
     { "xreal10.ua", dot_xfloat_cons_ua, 'x' },
@@ -5122,8 +5584,8 @@ pseudo_opcode[] =
     { "real8", stmt_float_cons, 'd' },
     { "real10", stmt_float_cons, 'x' },
     { "real16", stmt_float_cons, 'X' },
     { "real8", stmt_float_cons, 'd' },
     { "real10", stmt_float_cons, 'x' },
     { "real16", stmt_float_cons, 'X' },
-    { "string", stringer, 0 },
-    { "stringz", stringer, 1 },
+    { "string", stringer, 8 + 0 },
+    { "stringz", stringer, 8 + 1 },
 
     /* unaligned versions:  */
     { "data2.ua", stmt_cons_ua, 2 },
 
     /* unaligned versions:  */
     { "data2.ua", stmt_cons_ua, 2 },
@@ -5142,12 +5604,12 @@ pseudo_opcode[] =
 static symbolS *
 declare_register (name, regnum)
      const char *name;
 static symbolS *
 declare_register (name, regnum)
      const char *name;
-     int regnum;
+     unsigned int regnum;
 {
   const char *err;
   symbolS *sym;
 
 {
   const char *err;
   symbolS *sym;
 
-  sym = symbol_new (name, reg_section, regnum, &zero_address_frag);
+  sym = symbol_create (name, reg_section, regnum, &zero_address_frag);
 
   err = hash_insert (md.reg_hash, S_GET_NAME (sym), (PTR) sym);
   if (err)
 
   err = hash_insert (md.reg_hash, S_GET_NAME (sym), (PTR) sym);
   if (err)
@@ -5160,15 +5622,15 @@ declare_register (name, regnum)
 static void
 declare_register_set (prefix, num_regs, base_regnum)
      const char *prefix;
 static void
 declare_register_set (prefix, num_regs, base_regnum)
      const char *prefix;
-     int num_regs;
-     int base_regnum;
+     unsigned int num_regs;
+     unsigned int base_regnum;
 {
   char name[8];
 {
   char name[8];
-  int i;
+  unsigned int i;
 
   for (i = 0; i < num_regs; ++i)
     {
 
   for (i = 0; i < num_regs; ++i)
     {
-      sprintf (name, "%s%u", prefix, i);
+      snprintf (name, sizeof (name), "%s%u", prefix, i);
       declare_register (name, base_regnum + i);
     }
 }
       declare_register (name, base_regnum + i);
     }
 }
@@ -5448,6 +5910,17 @@ operand_match (idesc, index, e)
        return OPERAND_MATCH;
       break;
 
        return OPERAND_MATCH;
       break;
 
+    case IA64_OPND_IMMU5b:
+      if (e->X_op == O_constant)
+       {
+         val = e->X_add_number;
+         if (val >= 32 && val <= 63)
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
+      break;
+
     case IA64_OPND_CCNT5:
     case IA64_OPND_CNT5:
     case IA64_OPND_CNT6:
     case IA64_OPND_CCNT5:
     case IA64_OPND_CNT5:
     case IA64_OPND_CNT6:
@@ -5690,7 +6163,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
        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];
          fix->code = BFD_RELOC_UNUSED;
          fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code);
          fix->opnd = idesc->operands[index];
@@ -5720,27 +6193,19 @@ operand_match (idesc, index, e)
 }
 
 static int
 }
 
 static int
-parse_operand (e)
+parse_operand (e, more)
      expressionS *e;
      expressionS *e;
+     int more;
 {
   int sep = '\0';
 
   memset (e, 0, sizeof (*e));
   e->X_op = O_absent;
   SKIP_WHITESPACE ();
 {
   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_and_evaluate (e);
+  sep = *input_line_pointer;
+  if (more && (sep == ',' || sep == more))
+    ++input_line_pointer;
   return sep;
 }
 
   return sep;
 }
 
@@ -5765,6 +6230,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 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];
   enum ia64_opnd expected_operand = IA64_OPND_NIL;
   enum operand_match_result result;
   char mnemonic[129];
@@ -5774,7 +6241,8 @@ parse_operands (idesc)
   assert (strlen (idesc->name) <= 128);
 
   strcpy (mnemonic, idesc->name);
   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
     {
       /* 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
@@ -5792,11 +6260,22 @@ parse_operands (idesc)
       ++num_outputs;
     }
 
       ++num_outputs;
     }
 
-  for (; i < NELEMS (CURR_SLOT.opnd); ++i)
+  for (; ; ++i)
     {
     {
-      sep = parse_operand (CURR_SLOT.opnd + i);
-      if (CURR_SLOT.opnd[i].X_op == O_absent)
-       break;
+      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;
 
 
       ++num_operands;
 
@@ -5817,44 +6296,59 @@ parse_operands (idesc)
       return 0;
     }
 
       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 */
+      /* Map alloc r1=ar.pfs,i,l,o,r to alloc r1=ar.pfs,(i+l+o),(i+l),r.
+        Note, however, that due to that mapping operand numbers in error
+        messages for any of the constant operands will not be correct.  */
       know (strcmp (idesc->name, "alloc") == 0);
       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);
-
-         /* 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);
-         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];
-       }
+      /* The first operand hasn't been parsed/initialized, yet (but
+        num_operands intentionally doesn't account for that).  */
+      i = num_operands > 4 ? 2 : 1;
+#define FORCE_CONST(n) (CURR_SLOT.opnd[n].X_op == O_constant \
+                       ? CURR_SLOT.opnd[n].X_add_number \
+                       : 0)
+      sof = set_regstack (FORCE_CONST(i),
+                         FORCE_CONST(i + 1),
+                         FORCE_CONST(i + 2),
+                         FORCE_CONST(i + 3));
+#undef FORCE_CONST
+
+      /* 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, '=');
+      if (sep != '=')
+       --num_outputs;  /* force error */
+      input_line_pointer = saved_input_pointer;
+
+      CURR_SLOT.opnd[i].X_add_number = sof;
+      if (CURR_SLOT.opnd[i + 1].X_op == O_constant
+         && CURR_SLOT.opnd[i + 2].X_op == O_constant)
+       CURR_SLOT.opnd[i + 1].X_add_number
+         = sof - CURR_SLOT.opnd[i + 2].X_add_number;
+      else
+       CURR_SLOT.opnd[i + 1].X_op = O_illegal;
+      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;
   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 */
   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;
 
 
       CURR_SLOT.num_fixups = 0;
 
@@ -5907,10 +6401,6 @@ parse_operands (idesc)
          continue;
        }
 
          continue;
        }
 
-      if (num_operands < NELEMS (idesc->operands)
-         && idesc->operands[num_operands])
-       continue;               /* mismatch in number of arguments */
-
       break;
     }
   if (!idesc)
       break;
     }
   if (!idesc)
@@ -5919,103 +6409,136 @@ parse_operands (idesc)
        as_bad ("Operand %u of `%s' should be %s",
                error_pos + 1, mnemonic,
                elf64_ia64_operands[expected_operand].desc);
        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;
     }
       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)
+  /* 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_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;
-         }
+      int regno = 0;
+
+      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;
+       }
     }
     }
-
-  /* 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])
+  if (reg1 == reg2)
     {
     {
-      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;
-         }
-    }
-
-  /* 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;
+      if (reg1 >= REG_GR && reg1 <= REG_GR + 127)
+       {
+         reg1 -= REG_GR;
+         reg_class = 'r';
        }
        }
-    }
-  return 0;
+      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
 }
 
 static void
@@ -6025,7 +6548,8 @@ build_insn (slot, insnp)
 {
   const struct ia64_operand *odesc, *o2desc;
   struct ia64_opcode *idesc = slot->idesc;
 {
   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;
 
   const char *err;
   int i;
 
@@ -6145,18 +6669,17 @@ build_insn (slot, insnp)
 static void
 emit_one_bundle ()
 {
 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;
   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_vma t0 = 0, t1 = 0;
   struct label_fix *lfix;
+  bfd_boolean mark_label;
   struct insn_fix *ifix;
   char mnemonic[16];
   fixS *fix;
   struct insn_fix *ifix;
   char mnemonic[16];
   fixS *fix;
@@ -6164,7 +6687,7 @@ emit_one_bundle ()
   int addr_mod;
 
   first = (md.curr_slot + NUM_SLOTS - md.num_slots_in_use) % NUM_SLOTS;
   int addr_mod;
 
   first = (md.curr_slot + NUM_SLOTS - md.num_slots_in_use) % NUM_SLOTS;
-  know (first >= 0 & first < NUM_SLOTS);
+  know (first >= 0 && first < NUM_SLOTS);
   n = MIN (3, md.num_slots_in_use);
 
   /* Determine template: user user_template if specified, best match
   n = MIN (3, md.num_slots_in_use);
 
   /* Determine template: user user_template if specified, best match
@@ -6205,10 +6728,13 @@ emit_one_bundle ()
   curr = first;
   idesc = md.slot[curr].idesc;
   end_of_insn_group = 0;
   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.  */
   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
       if (ptr)
        {
          /* Find the last prologue/body record in the list for the current
@@ -6218,9 +6744,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.  */
             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;
            if (ptr->r.type == prologue || ptr->r.type == prologue_gr
                || ptr->r.type == body)
              last_ptr = ptr;
@@ -6241,13 +6769,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
          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)
        {
        }
       if (idesc->flags & IA64_OPCODE_LAST)
        {
@@ -6280,10 +6825,19 @@ emit_one_bundle ()
              required_slot = i;
              break;
            }
              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;
          if (required_slot < i)
            /* Can't fit this instruction.  */
            break;
@@ -6297,29 +6851,25 @@ emit_one_bundle ()
                 changing NOPs in front of this slot.  */
              for (j = i; j < 3; ++j)
                insn[j] = nop[ia64_templ_desc[required_template].exec_unit[j]];
                 changing NOPs in front of this slot.  */
              for (j = i; j < 3; ++j)
                insn[j] = nop[ia64_templ_desc[required_template].exec_unit[j]];
+
+             /* We just picked a template that includes the stop bit in the
+                middle, so we don't need another one emitted later.  */
+             md.slot[curr].end_of_insn_group = 0;
            }
          template = required_template;
        }
       if (curr != first && md.slot[curr].label_fixups)
        {
            }
          template = required_template;
        }
       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");
                          "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;
        }
 
          /* 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
       if (end_of_insn_group && md.num_slots_in_use >= 1)
        {
          /* We need an instruction group boundary in the middle of a
@@ -6347,12 +6897,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
                      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;
            }
                   && (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;
          else if (curr != first)
            /* can't fit this insn */
            break;
@@ -6373,9 +6928,34 @@ emit_one_bundle ()
          enum ia64_opnd opnd1, opnd2;
 
          if ((strcmp (idesc->name, "nop") == 0)
          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;
              || (strcmp (idesc->name, "break") == 0))
            insn_unit = required_unit;
+         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)
            {
          else if (strcmp (idesc->name, "chk.s") == 0
              || strcmp (idesc->name, "mov") == 0)
            {
@@ -6387,7 +6967,8 @@ emit_one_bundle ()
          else
            as_fatal ("emit_one_bundle: unexpected dynamic op");
 
          else
            as_fatal ("emit_one_bundle: unexpected dynamic op");
 
-         sprintf (mnemonic, "%s.%c", idesc->name, "?imbf??"[insn_unit]);
+         snprintf (mnemonic, sizeof (mnemonic), "%s.%c",
+                   idesc->name, "?imbfxx"[insn_unit]);
          opnd1 = idesc->operands[0];
          opnd2 = idesc->operands[1];
          ia64_free_opcode (idesc);
          opnd1 = idesc->operands[0];
          opnd2 = idesc->operands[1];
          ia64_free_opcode (idesc);
@@ -6400,11 +6981,6 @@ emit_one_bundle ()
                         || idesc->operands[1] != opnd2))
                idesc = get_next_opcode (idesc);
            }
                         || idesc->operands[1] != opnd2))
                idesc = get_next_opcode (idesc);
            }
-#if 0
-         else
-           /* no other resolved dynamic ops have collisions */
-           know (!get_next_opcode (idesc));
-#endif
          md.slot[curr].idesc = idesc;
        }
       else
          md.slot[curr].idesc = idesc;
        }
       else
@@ -6427,39 +7003,35 @@ emit_one_bundle ()
        }
 
       if (insn_unit != required_unit)
        }
 
       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);
        }
 
        }
 
-      if (debug_type == DEBUG_DWARF2 || md.slot[curr].loc_directive_seen)
+      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;
 
          md.slot[curr].loc_directive_seen = 0;
        {
          bfd_vma addr = frag_now->fr_address + frag_now_fix () - 16 + i;
 
          md.slot[curr].loc_directive_seen = 0;
+         if (mark_label)
+           md.slot[curr].debug_line.flags |= DWARF2_FLAG_BASIC_BLOCK;
+
          dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
        }
 
          dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
        }
 
-      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"));
-
       build_insn (md.slot + curr, insn + i);
 
       ptr = md.slot[curr].unwind_record;
       build_insn (md.slot + curr, insn + i);
 
       ptr = md.slot[curr].unwind_record;
@@ -6468,7 +7040,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.  */
          /* 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;
          for (; ptr != end_ptr; ptr = ptr->next)
            {
              ptr->slot_number = (unsigned long) f + i;
@@ -6484,19 +7055,7 @@ emit_one_bundle ()
          ++i;
        }
       --md.num_slots_in_use;
          ++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)
        {
 
       for (j = 0; j < md.slot[curr].num_fixups; ++j)
        {
@@ -6504,19 +7063,12 @@ emit_one_bundle ()
          fix = fix_new_exp (frag_now, frag_now_fix () - 16 + i, 8,
                             &ifix->expr, ifix->is_pcrel, ifix->code);
          fix->tc_fix_data.opnd = ifix->opnd;
          fix = fix_new_exp (frag_now, frag_now_fix () - 16 + i, 8,
                             &ifix->expr, ifix->is_pcrel, ifix->code);
          fix->tc_fix_data.opnd = ifix->opnd;
-         fix->fx_plt = (fix->fx_r_type == BFD_RELOC_IA64_PLTOFF22);
          fix->fx_file = md.slot[curr].src_file;
          fix->fx_line = md.slot[curr].src_line;
        }
 
       end_of_insn_group = md.slot[curr].end_of_insn_group;
 
          fix->fx_file = md.slot[curr].src_file;
          fix->fx_line = md.slot[curr].src_line;
        }
 
       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]));
       /* clear slot:  */
       ia64_free_opcode (md.slot[curr].idesc);
       memset (md.slot + curr, 0, sizeof (md.slot[curr]));
@@ -6530,19 +7082,48 @@ emit_one_bundle ()
       curr = (curr + 1) % NUM_SLOTS;
       idesc = md.slot[curr].idesc;
     }
       curr = (curr + 1) % NUM_SLOTS;
       idesc = md.slot[curr].idesc;
     }
-  if (manual_bundling)
+
+  /* A user template was specified, but the first following instruction did
+     not fit.  This can happen with or without manual bundling.  */
+  if (md.num_slots_in_use > 0 && 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 if (manual_bundling > 0)
     {
       if (md.num_slots_in_use > 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);
-         --md.num_slots_in_use;
+         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
+           {
+             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");
     }
        }
       else
        as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
                      "Missing '}' at end of file");
     }
+       
   know (md.num_slots_in_use < NUM_SLOTS);
 
   t0 = end_of_insn_group | (template << 1) | (insn[0] << 5) | (insn[1] << 46);
   know (md.num_slots_in_use < NUM_SLOTS);
 
   t0 = end_of_insn_group | (template << 1) | (insn[0] << 5) | (insn[1] << 46);
@@ -6550,12 +7131,6 @@ emit_one_bundle ()
 
   number_to_chars_littleendian (f + 0, t0, 8);
   number_to_chars_littleendian (f + 8, t1, 8);
 
   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
 }
 
 int
@@ -6588,6 +7163,38 @@ md_parse_option (c, arg)
          md.flags |= EF_IA_64_BE;
          default_big_endian = 1;
        }
          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;
       else
        return 0;
       break;
@@ -6643,6 +7250,10 @@ md_parse_option (c, arg)
        {
          md.default_explicit_mode = 0;
        }
        {
          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;
       else if (strcmp (arg, "debug") == 0)
        {
          md.debug_dv = 1;
@@ -6652,6 +7263,11 @@ md_parse_option (c, arg)
          md.default_explicit_mode = 1;
          md.debug_dv = 1;
        }
          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);
       else
        {
          as_bad (_("Unrecognized option '-x%s'"), arg);
@@ -6691,9 +7307,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\
                          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);
 }
 
        stream);
 }
 
@@ -6729,17 +7356,37 @@ match (int templ, int type, int slot)
   return result;
 }
 
   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)
 {
 
 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
 }
 
 /* This function is called once, at assembler startup time.  It sets
@@ -6748,7 +7395,7 @@ extra_goodness (int templ, int slot)
 void
 md_begin ()
 {
 void
 md_begin ()
 {
-  int i, j, k, t, total, ar_base, cr_base, goodness, best, regnum, ok;
+  int i, j, k, t, goodness, best, ok;
   const char *err;
   char name[8];
 
   const char *err;
   char name[8];
 
@@ -6834,11 +7481,17 @@ md_begin ()
     symbol_new (".<iplt>", undefined_section, FUNC_IPLT_RELOC,
                &zero_address_frag);
 
     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
   /* 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)
   for (i = 0; i < IA64_NUM_TYPES; ++i)
     for (j = 0; j < IA64_NUM_TYPES; ++j)
       for (k = 0; k < IA64_NUM_TYPES; ++k)
@@ -6851,7 +7504,7 @@ md_begin ()
                {
                  if (match (t, j, 1))
                    {
                {
                  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);
                        goodness = 3 + 3 + 3;
                      else
                        goodness = 3 + 3 + extra_goodness (t, 2);
@@ -6867,7 +7520,7 @@ md_begin ()
                }
              else if (match (t, i, 1))
                {
                }
              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);
                    goodness = 3 + 3;
                  else
                    goodness = 3 + extra_goodness (t, 2);
@@ -6883,6 +7536,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;
 
   for (i = 0; i < NUM_SLOTS; ++i)
     md.slot[i].user_template = -1;
 
@@ -6902,91 +7570,52 @@ md_begin ()
   md.entry_hash = hash_new ();
 
   /* general registers:  */
   md.entry_hash = hash_new ();
 
   /* general registers:  */
-
-  total = 128;
-  for (i = 0; i < total; ++i)
-    {
-      sprintf (name, "r%d", i - REG_GR);
-      md.regsym[i] = declare_register (name, i);
-    }
+  declare_register_set ("r", 128, REG_GR);
+  declare_register ("gp", REG_GR +  1);
+  declare_register ("sp", REG_GR + 12);
+  declare_register ("tp", REG_GR + 13);
+  declare_register_set ("ret", 4, REG_GR + 8);
 
   /* floating point registers:  */
 
   /* floating point registers:  */
-  total += 128;
-  for (; i < total; ++i)
-    {
-      sprintf (name, "f%d", i - REG_FR);
-      md.regsym[i] = declare_register (name, i);
-    }
-
-  /* application registers:  */
-  total += 128;
-  ar_base = i;
-  for (; i < total; ++i)
-    {
-      sprintf (name, "ar%d", i - REG_AR);
-      md.regsym[i] = declare_register (name, i);
-    }
+  declare_register_set ("f", 128, REG_FR);
+  declare_register_set ("farg", 8, REG_FR + 8);
+  declare_register_set ("fret", 8, REG_FR + 8);
 
 
-  /* control registers:  */
-  total += 128;
-  cr_base = i;
-  for (; i < total; ++i)
-    {
-      sprintf (name, "cr%d", i - REG_CR);
-      md.regsym[i] = declare_register (name, i);
-    }
+  /* branch registers:  */
+  declare_register_set ("b", 8, REG_BR);
+  declare_register ("rp", REG_BR + 0);
 
   /* predicate registers:  */
 
   /* predicate registers:  */
-  total += 64;
-  for (; i < total; ++i)
-    {
-      sprintf (name, "p%d", i - REG_P);
-      md.regsym[i] = declare_register (name, i);
-    }
+  declare_register_set ("p", 64, REG_P);
+  declare_register ("pr", REG_PR);
+  declare_register ("pr.rot", REG_PR_ROT);
 
 
-  /* branch registers:  */
-  total += 8;
-  for (; i < total; ++i)
-    {
-      sprintf (name, "b%d", i - REG_BR);
-      md.regsym[i] = declare_register (name, i);
-    }
+  /* application registers:  */
+  declare_register_set ("ar", 128, REG_AR);
+  for (i = 0; i < NELEMS (ar); ++i)
+    declare_register (ar[i].name, REG_AR + ar[i].regnum);
+
+  /* control registers:  */
+  declare_register_set ("cr", 128, REG_CR);
+  for (i = 0; i < NELEMS (cr); ++i)
+    declare_register (cr[i].name, REG_CR + cr[i].regnum);
 
 
-  md.regsym[REG_IP] = declare_register ("ip", REG_IP);
-  md.regsym[REG_CFM] = declare_register ("cfm", REG_CFM);
-  md.regsym[REG_PR] = declare_register ("pr", REG_PR);
-  md.regsym[REG_PR_ROT] = declare_register ("pr.rot", REG_PR_ROT);
-  md.regsym[REG_PSR] = declare_register ("psr", REG_PSR);
-  md.regsym[REG_PSR_L] = declare_register ("psr.l", REG_PSR_L);
-  md.regsym[REG_PSR_UM] = declare_register ("psr.um", REG_PSR_UM);
+  declare_register ("ip", REG_IP);
+  declare_register ("cfm", REG_CFM);
+  declare_register ("psr", REG_PSR);
+  declare_register ("psr.l", REG_PSR_L);
+  declare_register ("psr.um", REG_PSR_UM);
 
   for (i = 0; i < NELEMS (indirect_reg); ++i)
     {
 
   for (i = 0; i < NELEMS (indirect_reg); ++i)
     {
-      regnum = indirect_reg[i].regnum;
-      md.regsym[regnum] = declare_register (indirect_reg[i].name, regnum);
-    }
-
-  /* define synonyms for application registers:  */
-  for (i = REG_AR; i < REG_AR + NELEMS (ar); ++i)
-    md.regsym[i] = declare_register (ar[i - REG_AR].name,
-                                    REG_AR + ar[i - REG_AR].regnum);
+      unsigned int regnum = indirect_reg[i].regnum;
 
 
-  /* define synonyms for control registers:  */
-  for (i = REG_CR; i < REG_CR + NELEMS (cr); ++i)
-    md.regsym[i] = declare_register (cr[i - REG_CR].name,
-                                    REG_CR + cr[i - REG_CR].regnum);
-
-  declare_register ("gp", REG_GR +  1);
-  declare_register ("sp", REG_GR + 12);
-  declare_register ("rp", REG_BR +  0);
+      md.indregsym[regnum - IND_CPUID] = declare_register (indirect_reg[i].name, regnum);
+    }
 
   /* pseudo-registers used to specify unwind info:  */
   declare_register ("psp", REG_PSP);
 
 
   /* pseudo-registers used to specify unwind info:  */
   declare_register ("psp", REG_PSP);
 
-  declare_register_set ("ret", 4, REG_GR + 8);
-  declare_register_set ("farg", 8, REG_FR + 8);
-  declare_register_set ("fret", 8, REG_FR + 8);
-
   for (i = 0; i < NELEMS (const_bits); ++i)
     {
       err = hash_insert (md.const_hash, const_bits[i].name,
   for (i = 0; i < NELEMS (const_bits); ++i)
     {
       err = hash_insert (md.const_hash, const_bits[i].name,
@@ -7025,10 +7654,9 @@ md_begin ()
   md.entry_labels = NULL;
 }
 
   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)
 
 void
 ia64_init (argc, argv)
@@ -7036,6 +7664,11 @@ ia64_init (argc, argv)
      char **argv ATTRIBUTE_UNUSED;
 {
   md.flags = MD_FLAGS_DEFAULT;
      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.  */
 }
 
 /* Return a string for the target object file format.  */
@@ -7101,6 +7734,15 @@ ia64_end_of_source ()
 void
 ia64_start_line ()
 {
 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;
   if (md.qp.X_op == O_register)
     as_bad ("qualifying predicate not followed by instruction");
   md.qp.X_op = O_absent;
@@ -7111,10 +7753,52 @@ ia64_start_line ()
   if (input_line_pointer[0] == ';' && input_line_pointer[-1] == ';')
     {
       if (md.detect_dv && !md.explicit_mode)
   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);
     }
       else
        insn_group_break (1, 0, 0);
     }
+  else if (input_line_pointer[-1] == '{')
+    {
+      if (md.manual_bundling)
+       as_warn ("Found '{' when manual bundling is already turned on");
+      else
+       CURR_SLOT.manual_bundling_on = 1;
+      md.manual_bundling = 1;
+
+      /* Bundling is only acceptable in explicit mode
+        or when in default automatic mode.  */
+      if (md.detect_dv && !md.explicit_mode)
+       {
+         if (!md.mode_explicitly_set
+             && !md.default_explicit_mode)
+           dot_dv_mode ('E');
+         else
+           as_warn (_("Found '{' after explicit switch to automatic mode"));
+       }
+    }
+  else if (input_line_pointer[-1] == '}')
+    {
+      if (!md.manual_bundling)
+       as_warn ("Found '}' when manual bundling is off");
+      else
+       PREV_SLOT.manual_bundling_off = 1;
+      md.manual_bundling = 0;
+
+      /* switch back to automatic mode, if applicable */
+      if (md.detect_dv
+         && md.explicit_mode
+         && !md.mode_explicitly_set
+         && !md.default_explicit_mode)
+       dot_dv_mode ('A');
+    }
 }
 
 /* This is a hook for ia64_frob_label, so that it can distinguish tags from
 }
 
 /* This is a hook for ia64_frob_label, so that it can distinguish tags from
@@ -7128,7 +7812,7 @@ ia64_unrecognized_line (ch)
   switch (ch)
     {
     case '(':
   switch (ch)
     {
     case '(':
-      expression (&md.qp);
+      expression_and_evaluate (&md.qp);
       if (*input_line_pointer++ != ')')
        {
          as_bad ("Expected ')'");
       if (*input_line_pointer++ != ')')
        {
          as_bad ("Expected ')'");
@@ -7136,59 +7820,14 @@ ia64_unrecognized_line (ch)
        }
       if (md.qp.X_op != O_register)
        {
        }
       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 '{':
-      if (md.manual_bundling)
-       as_warn ("Found '{' when manual bundling is already turned on");
-      else
-       CURR_SLOT.manual_bundling_on = 1;
-      md.manual_bundling = 1;
-
-      /* Bundling is only acceptable in explicit mode
-        or when in default automatic mode.  */
-      if (md.detect_dv && !md.explicit_mode)
-       {
-         if (!md.mode_explicitly_set
-             && !md.default_explicit_mode)
-           dot_dv_mode ('E');
-         else
-           as_warn (_("Found '{' after explicit switch to automatic mode"));
+         as_bad ("Qualifying predicate expected");
+         return 0;
        }
        }
-      return 1;
-
-    case '}':
-      if (!md.manual_bundling)
-       as_warn ("Found '}' when manual bundling is off");
-      else
-       PREV_SLOT.manual_bundling_off = 1;
-      md.manual_bundling = 0;
-
-      /* switch back to automatic mode, if applicable */
-      if (md.detect_dv
-         && md.explicit_mode
-         && !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] == '{')
+      if (md.qp.X_add_number < REG_P || md.qp.X_add_number >= REG_P + 64)
        {
        {
-         input_line_pointer++;
-         return ia64_unrecognized_line ('{');
+         as_bad ("Predicate register expected");
+         return 0;
        }
        }
-
-      demand_empty_rest_of_line ();
       return 1;
 
     case '[':
       return 1;
 
     case '[':
@@ -7273,6 +7912,7 @@ ia64_frob_label (sym)
       fix = obstack_alloc (&notes, sizeof (*fix));
       fix->sym = sym;
       fix->next = CURR_SLOT.tag_fixups;
       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;
       CURR_SLOT.tag_fixups = fix;
 
       return;
@@ -7284,6 +7924,7 @@ ia64_frob_label (sym)
       fix = obstack_alloc (&notes, sizeof (*fix));
       fix->sym = sym;
       fix->next = CURR_SLOT.label_fixups;
       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.  */
       CURR_SLOT.label_fixups = fix;
 
       /* Keep track of how many code entry points we've seen.  */
@@ -7338,52 +7979,148 @@ ia64_optimize_expr (l, op, r)
      operatorT op;
      expressionS *r;
 {
      operatorT op;
      expressionS *r;
 {
-  unsigned num_regs;
-
-  if (op == O_index)
+  if (op != O_index)
+    return 0;
+  resolve_expression (l);
+  if (l->X_op == O_register)
     {
     {
-      if (l->X_op == O_register && r->X_op == O_constant)
+      unsigned num_regs = l->X_add_number >> 16;
+
+      resolve_expression (r);
+      if (num_regs)
        {
        {
-         num_regs = (l->X_add_number >> 16);
-         if ((unsigned) r->X_add_number >= num_regs)
+         /* Left side is a .rotX-allocated register.  */
+         if (r->X_op != O_constant)
            {
            {
-             if (!num_regs)
-               as_bad ("No current frame");
-             else
-               as_bad ("Index out of range 0..%u", num_regs - 1);
+             as_bad ("Rotating register index must be a non-negative constant");
+             r->X_add_number = 0;
+           }
+         else if ((valueT) r->X_add_number >= num_regs)
+           {
+             as_bad ("Index out of range 0..%u", num_regs - 1);
              r->X_add_number = 0;
            }
          l->X_add_number = (l->X_add_number & 0xffff) + r->X_add_number;
          return 1;
        }
              r->X_add_number = 0;
            }
          l->X_add_number = (l->X_add_number & 0xffff) + r->X_add_number;
          return 1;
        }
-      else if (l->X_op == O_register && r->X_op == O_register)
+      else if (l->X_add_number >= IND_CPUID && l->X_add_number <= IND_RR)
        {
        {
-         if (l->X_add_number < IND_CPUID || l->X_add_number > IND_RR
-             || l->X_add_number == IND_MEM)
+         if (r->X_op != O_register
+             || r->X_add_number < REG_GR
+             || r->X_add_number > REG_GR + 127)
            {
            {
-             as_bad ("Indirect register set name expected");
-             l->X_add_number = IND_CPUID;
+             as_bad ("Indirect register index must be a general register");
+             r->X_add_number = REG_GR;
            }
          l->X_op = O_index;
            }
          l->X_op = O_index;
-         l->X_op_symbol = md.regsym[l->X_add_number];
+         l->X_op_symbol = md.indregsym[l->X_add_number - IND_CPUID];
          l->X_add_number = r->X_add_number;
          return 1;
        }
     }
          l->X_add_number = r->X_add_number;
          return 1;
        }
     }
-  return 0;
+  as_bad ("Index can only be applied to rotating or indirect registers");
+  /* Fall back to some register use of which has as little as possible
+     side effects, to minimize subsequent error messages.  */
+  l->X_op = O_register;
+  l->X_add_number = REG_GR + 3;
+  return 1;
 }
 
 int
 }
 
 int
-ia64_parse_name (name, e)
+ia64_parse_name (name, e, nextcharP)
      char *name;
      expressionS *e;
      char *name;
      expressionS *e;
+     char *nextcharP;
 {
   struct const_desc *cdesc;
   struct dynreg *dr = 0;
 {
   struct const_desc *cdesc;
   struct dynreg *dr = 0;
-  unsigned int regnum;
+  unsigned int idx;
   struct symbol *sym;
   char *end;
 
   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)
   /* first see if NAME is a known register name:  */
   sym = hash_find (md.reg_hash, name);
   if (sym)
@@ -7402,13 +8139,14 @@ ia64_parse_name (name, e)
     }
 
   /* check for inN, locN, or outN:  */
     }
 
   /* check for inN, locN, or outN:  */
+  idx = 0;
   switch (name[0])
     {
     case 'i':
       if (name[1] == 'n' && ISDIGIT (name[2]))
        {
          dr = &md.in;
   switch (name[0])
     {
     case 'i':
       if (name[1] == 'n' && ISDIGIT (name[2]))
        {
          dr = &md.in;
-         name += 2;
+         idx = 2;
        }
       break;
 
        }
       break;
 
@@ -7416,7 +8154,7 @@ ia64_parse_name (name, e)
       if (name[1] == 'o' && name[2] == 'c' && ISDIGIT (name[3]))
        {
          dr = &md.loc;
       if (name[1] == 'o' && name[2] == 'c' && ISDIGIT (name[3]))
        {
          dr = &md.loc;
-         name += 3;
+         idx = 3;
        }
       break;
 
        }
       break;
 
@@ -7424,7 +8162,7 @@ ia64_parse_name (name, e)
       if (name[1] == 'u' && name[2] == 't' && ISDIGIT (name[3]))
        {
          dr = &md.out;
       if (name[1] == 'u' && name[2] == 't' && ISDIGIT (name[3]))
        {
          dr = &md.out;
-         name += 3;
+         idx = 3;
        }
       break;
 
        }
       break;
 
@@ -7432,13 +8170,16 @@ ia64_parse_name (name, e)
       break;
     }
 
       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.  */
       /* 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");
            {
              if (!dr->num_regs)
                as_bad ("No current frame");
@@ -7453,6 +8194,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.
   if ((dr = hash_find (md.dynreg_hash, name)))
     {
       /* We've got ourselves the name of a rotating register set.
@@ -7472,9 +8216,18 @@ char *
 ia64_canonicalize_symbol_name (name)
      char *name;
 {
 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;
 }
 
   return name;
 }
 
@@ -7585,6 +8338,8 @@ depends_on (depind, idesc)
    IC:rse-writers.
    15+16) Represents reserved instructions, which the assembler does not
    generate.
    IC:rse-writers.
    15+16) Represents reserved instructions, which the assembler does not
    generate.
+   17) CR[TPR] has a RAW dependency only between mov-to-CR-TPR and
+   mov-to-PSR-l or ssm instructions that set PSR.i, PSR.pp or PSR.up.
 
    Memory resources (i.e. locations in memory) are *not* marked or tracked by
    this code; there are no dependency violations based on memory access.
 
    Memory resources (i.e. locations in memory) are *not* marked or tracked by
    this code; there are no dependency violations based on memory access.
@@ -7620,9 +8375,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.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.specific = 1;
-  tmpl.index = 0;
+  tmpl.index = -1;
   tmpl.cmp_type = CMP_NONE;
   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"), \
 
 #define UNHANDLED \
 as_warn (_("Unhandled dependency %s for %s (%s), note %d"), \
@@ -8667,8 +9428,9 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
       break;
 
     case IA64_RS_CRX:
       break;
 
     case IA64_RS_CRX:
-      /* Handle all CR[REG] resources */
-      if (note == 0 || note == 1)
+      /* Handle all CR[REG] resources.
+        ??? FIXME: The rule 17 isn't really handled correctly.   */
+      if (note == 0 || note == 1 || note == 17)
        {
          if (idesc->operands[!rsrc_write] == IA64_OPND_CR3)
            {
        {
          if (idesc->operands[!rsrc_write] == IA64_OPND_CR3)
            {
@@ -8864,8 +9626,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)
                {
              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
                }
            }
          else
@@ -9319,6 +10080,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)
   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))
     {
           && (strcmp (idesc->name, "mov") == 0
               || strcmp (idesc->name, "movl") == 0))
     {
@@ -9336,6 +10098,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);
   else
     {
       clear_qp_mutex (qp_changemask);
@@ -9441,10 +10227,6 @@ resources_match (rs, idesc, note, qp_regno, path)
       else if (specs[count].index == rs->index)
        return 1;
     }
       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;
 }
 
   return 0;
 }
@@ -9560,7 +10342,7 @@ print_dependency (action, depind)
       fprintf (stderr, "  %s %s '%s'",
               action, dv_mode[(regdeps[depind].dependency)->mode],
               (regdeps[depind].dependency)->name);
       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)
        {
        fprintf (stderr, " (%d)", regdeps[depind].index);
       if (regdeps[depind].mem_offset.hint)
        {
@@ -9627,6 +10409,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));
          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;
          CURR_SLOT.idesc = ia64_find_opcode ("srlz.i");
          instruction_serialization ();
          md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
@@ -9648,6 +10431,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));
        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;
        CURR_SLOT.idesc = ia64_find_opcode ("srlz.d");
        data_serialization ();
        md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
@@ -9756,12 +10540,15 @@ check_dependencies (idesc)
              int certain = (matchtype == 1 && CURR_SLOT.qp_regno == 0);
 
              if (path != 0)
              int certain = (matchtype == 1 && CURR_SLOT.qp_regno == 0);
 
              if (path != 0)
-               sprintf (pathmsg, " when entry is at label '%s'",
+               snprintf (pathmsg, sizeof (pathmsg),
+                         " when entry is at label '%s'",
                         md.entry_labels[path - 1]);
                         md.entry_labels[path - 1]);
-             if (rs->specific && rs->index != 0)
-               sprintf (indexmsg, ", specific resource number is %d",
+             if (matchtype == 1 && rs->index >= 0)
+               snprintf (indexmsg, sizeof (indexmsg),
+                         ", specific resource number is %d",
                         rs->index);
                         rs->index);
-             sprintf (msg, "Use of '%s' %s %s dependency '%s' (%s)%s%s",
+             snprintf (msg, sizeof (msg),
+                       "Use of '%s' %s %s dependency '%s' (%s)%s%s",
                       idesc->name,
                       (certain ? "violates" : "may violate"),
                       dv_mode[dep->mode], dep->name,
                       idesc->name,
                       (certain ? "violates" : "may violate"),
                       dv_mode[dep->mode], dep->name,
@@ -9845,12 +10632,6 @@ mark_resources (idesc)
 
       count = specify_resource (dep, idesc, DV_REG, specs, note, md.path);
 
 
       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],
       while (count-- > 0)
        {
          mark_resource (idesc, dep, &specs[count],
@@ -10067,9 +10848,6 @@ md_assemble (str)
            mnemonic = "adds";
          ia64_free_opcode (idesc);
          idesc = ia64_find_opcode (mnemonic);
            mnemonic = "adds";
          ia64_free_opcode (idesc);
          idesc = ia64_find_opcode (mnemonic);
-#if 0
-         know (!idesc->next);
-#endif
        }
       else if (strcmp (idesc->name, "mov") == 0)
        {
        }
       else if (strcmp (idesc->name, "mov") == 0)
        {
@@ -10128,11 +10906,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])
          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));
        }
     }
                    (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)
 
   qp_regno = 0;
   if (md.qp.X_op == O_register)
@@ -10178,12 +10970,26 @@ md_assemble (str)
   as_where (&CURR_SLOT.src_file, &CURR_SLOT.src_line);
   dwarf2_where (&CURR_SLOT.debug_line);
 
   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.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)
 
   /* Check for dependency violations.  */
   if (md.detect_dv)
@@ -10220,110 +11026,25 @@ void
 md_operand (e)
      expressionS *e;
 {
 md_operand (e)
      expressionS *e;
 {
-  enum pseudo_type pseudo_type;
-  const char *name;
-  size_t len;
-  int ch, i;
-
   switch (*input_line_pointer)
     {
   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;
     case '[':
       ++input_line_pointer;
-      expression (e);
+      expression_and_evaluate (e);
       if (*input_line_pointer != ']')
        {
       if (*input_line_pointer != ']')
        {
-         as_bad ("Closing bracket misssing");
+         as_bad ("Closing bracket missing");
          goto err;
        }
       else
        {
          goto err;
        }
       else
        {
-         if (e->X_op != O_register)
-           as_bad ("Register expected as index");
+         if (e->X_op != O_register
+             || e->X_add_number < REG_GR
+             || e->X_add_number > REG_GR + 127)
+           {
+             as_bad ("Index must be a general register");
+             e->X_add_number = REG_GR;
+           }
 
          ++input_line_pointer;
          e->X_op = O_index;
 
          ++input_line_pointer;
          e->X_op = O_index;
@@ -10348,7 +11069,7 @@ ia64_fix_adjustable (fix)
      fixS *fix;
 {
   /* Prevent all adjustments to global symbols */
      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)
     return 0;
 
   switch (fix->fx_r_type)
@@ -10509,7 +11230,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
   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;
 }
 
   fix->tc_fix_data.bigendian = target_big_endian;
 }
 
@@ -10523,6 +11244,7 @@ ia64_gen_real_reloc_type (sym, r_type)
      bfd_reloc_code_real_type r_type;
 {
   bfd_reloc_code_real_type new = 0;
      bfd_reloc_code_real_type r_type;
 {
   bfd_reloc_code_real_type new = 0;
+  const char *type = NULL, *suffix = "";
 
   if (sym == NULL)
     {
 
   if (sym == NULL)
     {
@@ -10539,7 +11261,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;
        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;
 
        }
       break;
 
@@ -10552,7 +11274,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;
        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;
 
        }
       break;
 
@@ -10561,7 +11283,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;
        {
        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;
 
        }
       break;
 
@@ -10569,7 +11291,7 @@ ia64_gen_real_reloc_type (sym, r_type)
       switch (r_type)
        {
        case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_LTOFF22X; break;
       switch (r_type)
        {
        case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_LTOFF22X; break;
-       default:                        break;
+       default:                        type = "LTOFF"; suffix = "X"; break;
        }
       break;
 
        }
       break;
 
@@ -10582,7 +11304,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;
        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;
 
        }
       break;
 
@@ -10593,7 +11315,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;
        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;
 
        }
       break;
 
@@ -10604,7 +11326,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;
        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;
 
        }
       break;
 
@@ -10615,7 +11337,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;
        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;
 
        }
       break;
 
@@ -10626,7 +11348,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;
        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;
 
        }
       break;
 
@@ -10637,22 +11359,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;
          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:
        default:
-         break;
+         type = "LTOFF_FPTR"; break;
        }
       break;
 
     case FUNC_TP_RELATIVE:
       switch (r_type)
        {
        }
       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;
 
        }
       break;
 
@@ -10662,7 +11390,19 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM22:
          new = BFD_RELOC_IA64_LTOFF_TPREL22; break;
        default:
        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;
 
        }
       break;
 
@@ -10672,13 +11412,17 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM22:
          new = BFD_RELOC_IA64_LTOFF_DTPMOD22; break;
        default:
        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)
        {
        }
       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:
        case BFD_RELOC_IA64_DIR64MSB:
          new = BFD_RELOC_IA64_DTPREL64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:
@@ -10690,7 +11434,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM64:
          new = BFD_RELOC_IA64_DTPREL64I; break;
        default:
        case BFD_RELOC_IA64_IMM64:
          new = BFD_RELOC_IA64_DTPREL64I; break;
        default:
-         break;
+         type = "DTPREL"; break;
        }
       break;
 
        }
       break;
 
@@ -10700,22 +11444,49 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM22:
          new = BFD_RELOC_IA64_LTOFF_DTPREL22; break;
        default:
        case BFD_RELOC_IA64_IMM22:
          new = BFD_RELOC_IA64_LTOFF_DTPREL22; break;
        default:
-         break;
+         type = "LTOFF_DTPREL"; break;
        }
       break;
 
     case FUNC_IPLT_RELOC:
        }
       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 ();
     }
 
       break;
 
     default:
       abort ();
     }
 
-  /* Hmmmm.  Should this ever occur?  */
   if (new)
     return new;
   else
   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
 }
 
 /* Here is where generate the appropriate reloc for pseudo relocation
@@ -10806,7 +11577,7 @@ fix_insn (fix, odesc, value)
    (if possible).  */
 
 void
    (if possible).  */
 
 void
-md_apply_fix3 (fix, valP, seg)
+md_apply_fix (fix, valP, seg)
      fixS *fix;
      valueT *valP;
      segT seg ATTRIBUTE_UNUSED;
      fixS *fix;
      valueT *valP;
      segT seg ATTRIBUTE_UNUSED;
@@ -10818,27 +11589,24 @@ md_apply_fix3 (fix, valP, seg)
 
   if (fix->fx_pcrel)
     {
 
   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)
     {
     }
   if (fix->fx_addsy)
     {
@@ -10906,6 +11674,8 @@ tc_gen_reloc (sec, fixp)
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    "Cannot represent %s relocation in object file",
                    bfd_get_reloc_code_name (fixp->fx_r_type));
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    "Cannot represent %s relocation in object file",
                    bfd_get_reloc_code_name (fixp->fx_r_type));
+      free (reloc);
+      return NULL;
     }
   return reloc;
 }
     }
   return reloc;
 }
@@ -10993,14 +11763,6 @@ void
 ia64_handle_align (fragp)
      fragS *fragp;
 {
 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;
   int bytes;
   char *p;
   const unsigned char *nop;
@@ -11101,7 +11863,7 @@ struct alias
 {
   char *file;          /* The file where the directive is seen.  */
   unsigned int line;   /* The line number the directive is at.  */
 {
   char *file;          /* The file where the directive is seen.  */
   unsigned int line;   /* The line number the directive is at.  */
-  const char *name;    /* The orignale name of the symbol.  */
+  const char *name;    /* The original name of the symbol.  */
 };
 
 /* Called for .alias and .secalias directives. If SECTION is 1, it is
 };
 
 /* Called for .alias and .secalias directives. If SECTION is 1, it is
@@ -11127,7 +11889,7 @@ dot_alias (int section)
   if (name == end_name)
     {
       as_bad (_("expected symbol name"));
   if (name == end_name)
     {
       as_bad (_("expected symbol name"));
-      discard_rest_of_line ();
+      ignore_rest_of_line ();
       return;
     }
 
       return;
     }
 
@@ -11144,6 +11906,7 @@ dot_alias (int section)
 
   input_line_pointer++;
   *end_name = 0;
 
   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.  */
 
   /* We call demand_copy_C_string to check if alias string is valid.
      There should be a closing `"' and no `\0' in the string.  */