gas/
[deliverable/binutils-gdb.git] / gas / config / tc-ia64.c
index 9973b59a71b7a7fc392c0401926b0bd8ed897915..484b25c45e1329ab5d809c8ac640c7c5d535d793 100644 (file)
@@ -1,5 +1,6 @@
 /* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
-   Copyright 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+   Free Software Foundation, Inc.
    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
 
    This file is part of GAS, the GNU Assembler.
@@ -24,7 +25,6 @@
 
   - optional operands
   - directives:
-       .alias
        .eb
        .estate
        .lb
@@ -43,6 +43,7 @@
  */
 
 #include "as.h"
+#include "safe-ctype.h"
 #include "dwarf2dbg.h"
 #include "subsegs.h"
 
 
 #include "elf/ia64.h"
 
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
 #define NELEMS(a)      ((int) (sizeof (a)/sizeof ((a)[0])))
 #define MIN(a,b)       ((a) < (b) ? (a) : (b))
 
@@ -76,15 +81,23 @@ enum special_section
 
 enum reloc_func
   {
+    FUNC_DTP_MODULE,
+    FUNC_DTP_RELATIVE,
     FUNC_FPTR_RELATIVE,
     FUNC_GP_RELATIVE,
     FUNC_LT_RELATIVE,
+    FUNC_LT_RELATIVE_X,
     FUNC_PC_RELATIVE,
     FUNC_PLT_RELATIVE,
     FUNC_SEC_RELATIVE,
     FUNC_SEG_RELATIVE,
+    FUNC_TP_RELATIVE,
     FUNC_LTV_RELATIVE,
     FUNC_LT_FPTR_RELATIVE,
+    FUNC_LT_DTP_MODULE,
+    FUNC_LT_DTP_RELATIVE,
+    FUNC_LT_TP_RELATIVE,
+    FUNC_IPLT_RELOC,
   };
 
 enum reg_symbol
@@ -145,8 +158,30 @@ struct label_fix
   struct symbol *sym;
 };
 
+/* This is the endianness of the current section.  */
 extern int target_big_endian;
 
+/* This is the default endianness.  */
+static int default_big_endian = TARGET_BYTES_BIG_ENDIAN;
+
+void (*ia64_number_to_chars) PARAMS ((char *, valueT, int));
+
+static void ia64_float_to_chars_bigendian
+  PARAMS ((char *, LITTLENUM_TYPE *, int));
+static void ia64_float_to_chars_littleendian
+  PARAMS ((char *, LITTLENUM_TYPE *, int));
+static void (*ia64_float_to_chars)
+  PARAMS ((char *, LITTLENUM_TYPE *, int));
+
+static struct hash_control *alias_hash;
+static struct hash_control *alias_name_hash;
+static struct hash_control *secalias_hash;
+static struct hash_control *secalias_name_hash;
+
+/* List of chars besides those in app.c:symbol_chars that can start an
+   operand.  Used to prevent the scrubber eating vital white-space.  */
+const char ia64_symbol_chars[] = "@?";
+
 /* Characters which always start a comment.  */
 const char comment_chars[] = "";
 
@@ -194,6 +229,21 @@ static struct
        that are predicatable.  */
     expressionS qp;
 
+    /* Optimize for which CPU.  */
+    enum
+      {
+       itanium1,
+       itanium2
+      } tune;
+
+    /* What to do when hint.b is used.  */
+    enum
+      {
+       hint_b_error,
+       hint_b_warning,
+       hint_b_ok
+      } hint_b;
+
     unsigned int
       manual_bundling : 1,
       debug_dv: 1,
@@ -204,6 +254,13 @@ static struct
       auto_align : 1,
       keep_pending_output : 1;
 
+    /* What to do when something is wrong with unwind directives.  */
+    enum
+      {
+       unwind_check_warning,
+       unwind_check_error
+      } unwind_check;
+
     /* Each bundle consists of up to three instructions.  We keep
        track of four most recent instructions so we can correctly set
        the end_of_insn_group for the last instruction in a bundle.  */
@@ -214,7 +271,8 @@ static struct
        unsigned int
          end_of_insn_group : 1,
          manual_bundling_on : 1,
-         manual_bundling_off : 1;
+         manual_bundling_off : 1,
+         loc_directive_seen : 1;
        signed char user_template;      /* user-selected template, if any */
        unsigned char qp_regno;         /* qualifying predicate */
        /* This duplicates a good fraction of "struct fix" but we
@@ -264,31 +322,27 @@ static struct
                                       the current DV-checking block.  */
     int maxpaths;                   /* size currently allocated for
                                       entry_labels */
-    /* Support for hardware errata workarounds.  */
-
-    /* Record data about the last three insn groups.  */
-    struct group
-    {
-      /* B-step workaround.
-        For each predicate register, this is set if the corresponding insn
-        group conditionally sets this register with one of the affected
-        instructions.  */
-      int p_reg_set[64];
-      /* B-step workaround.
-        For each general register, this is set if the corresponding insn
-        a) is conditional one one of the predicate registers for which
-           P_REG_SET is 1 in the corresponding entry of the previous group,
-        b) sets this general register with one of the affected
-           instructions.  */
-      int g_reg_set_conditionally[128];
-    } last_groups[3];
-    int group_idx;
 
     int pointer_size;       /* size in bytes of a pointer */
     int pointer_size_shift; /* shift size of a pointer for alignment */
   }
 md;
 
+/* These are not const, because they are modified to MMI for non-itanium1
+   targets below.  */
+/* MFI bundle of nops.  */
+static unsigned char le_nop[16] =
+{
+  0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
+};
+/* MFI bundle of nops with stop-bit.  */
+static unsigned char le_nop_stop[16] =
+{
+  0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
+};
+
 /* application registers:  */
 
 #define AR_K0          0
@@ -474,15 +528,23 @@ static struct
 pseudo_func[] =
   {
     /* reloc pseudo functions (these must come first!):  */
+    { "dtpmod",        PSEUDO_FUNC_RELOC, { 0 } },
+    { "dtprel",        PSEUDO_FUNC_RELOC, { 0 } },
     { "fptr",  PSEUDO_FUNC_RELOC, { 0 } },
     { "gprel", PSEUDO_FUNC_RELOC, { 0 } },
     { "ltoff", PSEUDO_FUNC_RELOC, { 0 } },
+    { "ltoffx",        PSEUDO_FUNC_RELOC, { 0 } },
     { "pcrel", PSEUDO_FUNC_RELOC, { 0 } },
     { "pltoff",        PSEUDO_FUNC_RELOC, { 0 } },
     { "secrel",        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 */
+    { 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:  */
     { "alt",   PSEUDO_FUNC_CONST, { 0xa } },
@@ -504,10 +566,17 @@ pseudo_func[] =
 
     { "natval",        PSEUDO_FUNC_CONST, { 0x100 } }, /* old usage */
 
+    /* hint constants: */
+    { "pause", PSEUDO_FUNC_CONST, { 0x0 } },
+
     /* unwind-related constants:  */
-    { "svr4",  PSEUDO_FUNC_CONST, { 0 } },
-    { "hpux",  PSEUDO_FUNC_CONST, { 1 } },
-    { "nt",    PSEUDO_FUNC_CONST, { 2 } },
+    { "svr4",  PSEUDO_FUNC_CONST,      { ELFOSABI_NONE } },
+    { "hpux",  PSEUDO_FUNC_CONST,      { ELFOSABI_HPUX } },
+    { "nt",    PSEUDO_FUNC_CONST,      { 2 } },                /* conflicts w/ELFOSABI_NETBSD */
+    { "linux", PSEUDO_FUNC_CONST,      { ELFOSABI_LINUX } },
+    { "freebsd", PSEUDO_FUNC_CONST,    { ELFOSABI_FREEBSD } },
+    { "openvms", PSEUDO_FUNC_CONST,    { ELFOSABI_OPENVMS } },
+    { "nsk",   PSEUDO_FUNC_CONST,      { ELFOSABI_NSK } },
 
     /* unwind-related registers:  */
     { "priunat",PSEUDO_FUNC_REG, { REG_PRIUNAT } }
@@ -534,11 +603,6 @@ static char special_section_name[][20] =
     {".init_array"}, {".fini_array"}
   };
 
-static char *special_linkonce_name[] =
-  {
-    ".gnu.linkonce.ia64unw.", ".gnu.linkonce.ia64unwi."
-  };
-
 /* The best template for a particular sequence of up to three
    instructions:  */
 #define N      IA64_NUM_TYPES
@@ -597,7 +661,20 @@ static struct gr {
   unsigned known:1;
   int path;
   valueT value;
-} gr_values[128] = {{ 1, 0, 0 }};
+} gr_values[128] = {
+  {
+    1,
+#ifdef INT_MAX
+    INT_MAX,
+#else
+    (((1 << (8 * sizeof(gr_values->path) - 2)) - 1) << 1) + 1,
+#endif
+    0
+  }
+};
+
+/* Remember the alignment frag.  */
+static fragS *align_frag;
 
 /* These are the routines required to output the various types of
    unwind records.  */
@@ -615,16 +692,24 @@ typedef struct unw_rec_list {
   unwind_record r;
   unsigned long slot_number;
   fragS *slot_frag;
+  unsigned long next_slot_number;
+  fragS *next_slot_frag;
   struct unw_rec_list *next;
 } unw_rec_list;
 
 #define SLOT_NUM_NOT_SET        (unsigned)-1
 
-static struct
+/* Linked list of saved prologue counts.  A very poor
+   implementation of a map from label numbers to prologue counts.  */
+typedef struct label_prologue_count
 {
-  unsigned long next_slot_number;
-  fragS *next_slot_frag;
+  struct label_prologue_count *next;
+  unsigned long label_number;
+  unsigned int prologue_count;
+} label_prologue_count;
 
+static struct
+{
   /* Maintain a list of unwind entries for the current function.  */
   unw_rec_list *list;
   unw_rec_list *tail;
@@ -635,7 +720,6 @@ static struct
 
   /* These are used to create the unwind table entry for this function.  */
   symbolS *proc_start;
-  symbolS *proc_end;
   symbolS *info;               /* pointer to unwind info */
   symbolS *personality_routine;
   segT saved_text_seg;
@@ -643,18 +727,28 @@ static struct
   unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */
 
   /* TRUE if processing unwind directives in a prologue region.  */
-  int prologue;
-  int prologue_mask;
+  unsigned int prologue : 1;
+  unsigned int prologue_mask : 4;
+  unsigned int 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;
 } unwind;
 
+/* The input value is a negated offset from psp, and specifies an address
+   psp - offset.  The encoded value is psp + 16 - (4 * offset).  Thus we
+   must add 16 and divide by 4 to get the encoded value.  */
+
+#define ENCODED_PSP_OFFSET(OFFSET) (((OFFSET) + 16) / 4)
+
 typedef void (*vbyte_func) PARAMS ((int, char *, char *));
 
-/* Forward delarations:  */
-static int ar_is_in_integer_unit PARAMS ((int regnum));
+/* Forward declarations:  */
 static void set_section PARAMS ((char *name));
 static unsigned int set_regstack PARAMS ((unsigned int, unsigned int,
                                          unsigned int, unsigned int));
+static void dot_align (int);
 static void dot_radix PARAMS ((int));
 static void dot_special_section PARAMS ((int));
 static void dot_proc PARAMS ((int));
@@ -693,7 +787,7 @@ static void dot_byteorder PARAMS ((int));
 static void dot_psr PARAMS ((int));
 static void dot_alias PARAMS ((int));
 static void dot_ln PARAMS ((int));
-static char *parse_section_name PARAMS ((void));
+static void cross_section PARAMS ((int ref, void (*cons) PARAMS((int)), int ua));
 static void dot_xdata PARAMS ((int));
 static void stmt_float_cons PARAMS ((int));
 static void stmt_cons_ua PARAMS ((int));
@@ -704,6 +798,7 @@ static void dot_xfloat_cons_ua PARAMS ((int));
 static void print_prmask PARAMS ((valueT mask));
 static void dot_pred_rel PARAMS ((int));
 static void dot_reg_val PARAMS ((int));
+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));
@@ -729,6 +824,7 @@ static void add_qp_imply PARAMS((int p1, int p2));
 static void clear_qp_branch_flag PARAMS((valueT mask));
 static void clear_qp_mutex PARAMS((valueT mask));
 static void clear_qp_implies PARAMS((valueT p1_mask, valueT p2_mask));
+static int has_suffix_p PARAMS((const char *, const char *));
 static void clear_register_values PARAMS ((void));
 static void print_dependency PARAMS ((const char *action, int depind));
 static void instruction_serialization PARAMS ((void));
@@ -773,7 +869,7 @@ static void output_X2_format PARAMS ((vbyte_func, int, int, int, int, int, unsig
 static void output_X3_format PARAMS ((vbyte_func, unw_record_type, int, int, int, unsigned long,
                                      unsigned long));
 static void output_X4_format PARAMS ((vbyte_func, int, int, int, int, int, int, unsigned long));
-static void free_list_records PARAMS ((unw_rec_list *));
+static unw_rec_list *output_endp PARAMS ((void));
 static unw_rec_list *output_prologue PARAMS ((void));
 static unw_rec_list *output_prologue_gr PARAMS ((unsigned int, unsigned int));
 static unw_rec_list *output_body PARAMS ((void));
@@ -848,54 +944,33 @@ static void process_one_record PARAMS ((unw_rec_list *, vbyte_func));
 static void process_unw_records PARAMS ((unw_rec_list *, vbyte_func));
 static int calc_record_size PARAMS ((unw_rec_list *));
 static void set_imask PARAMS ((unw_rec_list *, unsigned long, unsigned long, unsigned int));
-static int count_bits PARAMS ((unsigned long));
 static unsigned long slot_index PARAMS ((unsigned long, fragS *,
-                                        unsigned long, fragS *));
+                                        unsigned long, fragS *,
+                                        int));
 static unw_rec_list *optimize_unw_records PARAMS ((unw_rec_list *));
-static void fixup_unw_records PARAMS ((unw_rec_list *));
-static int output_unw_records PARAMS ((unw_rec_list *, void **));
+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 generate_unwind_image PARAMS ((const char *));
-
-/* Build the unwind section name by appending the (possibly stripped)
-   text section NAME to the unwind PREFIX.  The resulting string
-   pointer is assigned to RESULT.  The string is allocated on the
-   stack, so this must be a macro... */
-#define make_unw_section_name(special, text_name, result)                 \
-  {                                                                       \
-    char *_prefix = special_section_name[special];                        \
-    char *_suffix = text_name;                                            \
-    size_t _prefix_len, _suffix_len;                                      \
-    char *_result;                                                        \
-    if (strncmp (text_name, ".gnu.linkonce.t.",                                   \
-                sizeof (".gnu.linkonce.t.") - 1) == 0)                    \
-      {                                                                           \
-       _prefix = special_linkonce_name[special - SPECIAL_SECTION_UNWIND]; \
-       _suffix += sizeof (".gnu.linkonce.t.") - 1;                        \
-      }                                                                           \
-    _prefix_len = strlen (_prefix), _suffix_len = strlen (_suffix);       \
-    _result = alloca (_prefix_len + _suffix_len + 1);                     \
-    memcpy (_result, _prefix, _prefix_len);                               \
-    memcpy (_result + _prefix_len, _suffix, _suffix_len);                 \
-    _result[_prefix_len + _suffix_len] = '\0';                            \
-    result = _result;                                                     \
-  }                                                                       \
-while (0)
-
-/* Determine if application register REGNUM resides in the integer
+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));
+
+/* Determine if application register REGNUM resides only in the integer
    unit (as opposed to the memory unit).  */
 static int
-ar_is_in_integer_unit (reg)
-     int reg;
+ar_is_only_in_integer_unit (int reg)
 {
   reg -= REG_AR;
+  return reg >= 64 && reg <= 111;
+}
 
-  return (reg == 64    /* pfs */
-         || reg == 65  /* lc */
-         || reg == 66  /* ec */
-         /* ??? ias accepts and puts these in the integer unit.  */
-         || (reg >= 112 && reg <= 127));
+/* Determine if application register REGNUM resides only in the memory 
+   unit (as opposed to the integer unit).  */
+static int
+ar_is_only_in_memory_unit (int reg)
+{
+  reg -= REG_AR;
+  return reg >= 0 && reg <= 47;
 }
 
 /* Switch to section NAME and create section if necessary.  It's
@@ -914,6 +989,22 @@ set_section (name)
   input_line_pointer = saved_input_line_pointer;
 }
 
+/* Map 's' to SHF_IA_64_SHORT.  */
+
+int
+ia64_elf_section_letter (letter, ptr_msg)
+     int letter;
+     char **ptr_msg;
+{
+  if (letter == 's')
+    return SHF_IA_64_SHORT;
+  else if (letter == 'o')
+    return SHF_LINK_ORDER;
+
+  *ptr_msg = _("Bad .section directive: want a,o,s,w,x,M,S,G,T in string");
+  return -1;
+}
+
 /* Map SHF_IA_64_SHORT to SEC_SMALL_DATA.  */
 
 flagword
@@ -928,26 +1019,28 @@ ia64_elf_section_flags (flags, attr, type)
 
 int
 ia64_elf_section_type (str, len)
-       const char *str;
-       size_t len;
+     const char *str;
+     size_t len;
 {
-  len = sizeof (ELF_STRING_ia64_unwind_info) - 1;
-  if (strncmp (str, ELF_STRING_ia64_unwind_info, len) == 0)
+#define STREQ(s) ((len == sizeof (s) - 1) && (strncmp (str, s, sizeof (s) - 1) == 0))
+
+  if (STREQ (ELF_STRING_ia64_unwind_info))
     return SHT_PROGBITS;
 
-  len = sizeof (ELF_STRING_ia64_unwind_info_once) - 1;
-  if (strncmp (str, ELF_STRING_ia64_unwind_info_once, len) == 0)
+  if (STREQ (ELF_STRING_ia64_unwind_info_once))
     return SHT_PROGBITS;
 
-  len = sizeof (ELF_STRING_ia64_unwind) - 1;
-  if (strncmp (str, ELF_STRING_ia64_unwind, len) == 0)
+  if (STREQ (ELF_STRING_ia64_unwind))
+    return SHT_IA_64_UNWIND;
+
+  if (STREQ (ELF_STRING_ia64_unwind_once))
     return SHT_IA_64_UNWIND;
 
-  len = sizeof (ELF_STRING_ia64_unwind_once) - 1;
-  if (strncmp (str, ELF_STRING_ia64_unwind_once, len) == 0)
+  if (STREQ ("unwind"))
     return SHT_IA_64_UNWIND;
 
   return -1;
+#undef STREQ
 }
 
 static unsigned int
@@ -1014,18 +1107,40 @@ ia64_flush_insns ()
   CURR_SLOT.tag_fixups = 0;
 
   /* In case there are unwind directives following the last instruction,
-     resolve those now.  We only handle body and prologue directives here.
-     Give an error for others.  */
+     resolve those now.  We only handle prologue, body, and endp directives
+     here.  Give an error for others.  */
   for (ptr = unwind.current_entry; ptr; ptr = ptr->next)
     {
-      if (ptr->r.type == prologue || ptr->r.type == prologue_gr
-         || ptr->r.type == body)
+      switch (ptr->r.type)
        {
+       case prologue:
+       case prologue_gr:
+       case body:
+       case endp:
          ptr->slot_number = (unsigned long) frag_more (0);
          ptr->slot_frag = frag_now;
+         break;
+
+         /* Allow any record which doesn't have a "t" field (i.e.,
+            doesn't relate to a particular instruction).  */
+       case unwabi:
+       case br_gr:
+       case copy_state:
+       case fr_mem:
+       case frgr_mem:
+       case gr_gr:
+       case gr_mem:
+       case label_state:
+       case rp_br:
+       case spill_base:
+       case spill_mask:
+         /* nothing */
+         break;
+
+       default:
+         as_bad (_("Unwind directive not followed by an instruction."));
+         break;
        }
-      else
-       as_bad (_("Unwind directive not followed by an instruction."));
     }
   unwind.current_entry = NULL;
 
@@ -1035,9 +1150,8 @@ ia64_flush_insns ()
     as_bad ("qualifying predicate not followed by instruction");
 }
 
-void
-ia64_do_align (nbytes)
-     int nbytes;
+static void
+ia64_do_align (int nbytes)
 {
   char *saved_input_line_pointer = input_line_pointer;
 
@@ -1060,7 +1174,7 @@ ia64_cons_align (nbytes)
 }
 
 /* Output COUNT bytes to a memory location.  */
-static unsigned char *vbyte_mem_ptr = NULL;
+static char *vbyte_mem_ptr = NULL;
 
 void
 output_vbyte_mem (count, ptr, comment)
@@ -1237,7 +1351,7 @@ output_P4_format (f, imask, imask_size)
      unsigned long imask_size;
 {
   imask[0] = UNW_P4;
-  (*f) (imask_size, imask, NULL);
+  (*f) (imask_size, (char *) imask, NULL);
 }
 
 static void
@@ -1633,26 +1747,19 @@ alloc_record (unw_record_type t)
   ptr->next = NULL;
   ptr->slot_number = SLOT_NUM_NOT_SET;
   ptr->r.type = t;
+  ptr->next_slot_number = 0;
+  ptr->next_slot_frag = 0;
   return ptr;
 }
 
-/* This function frees an entire list of record structures.  */
+/* Dummy unwind record used for calculating the length of the last prologue or
+   body region.  */
 
-void
-free_list_records (unw_rec_list *first)
+static unw_rec_list *
+output_endp ()
 {
-  unw_rec_list *ptr;
-  for (ptr = first; ptr != NULL;)
-    {
-      unw_rec_list *tmp = ptr;
-
-      if ((tmp->r.type == prologue || tmp->r.type == prologue_gr)
-         && tmp->r.record.r.mask.i)
-       free (tmp->r.record.r.mask.i);
-
-      ptr = ptr->next;
-      free (tmp);
-    }
+  unw_rec_list *ptr = alloc_record (endp);
+  return ptr;
 }
 
 static unw_rec_list *
@@ -1746,7 +1853,7 @@ output_rp_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rp_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1780,7 +1887,7 @@ output_pfs_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (pfs_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1814,7 +1921,7 @@ output_preds_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (preds_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1891,7 +1998,7 @@ output_spill_base (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (spill_base);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1916,7 +2023,7 @@ output_unat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (unat_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1950,7 +2057,7 @@ output_lc_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (lc_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -1984,7 +2091,7 @@ output_fpsr_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (fpsr_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2025,7 +2132,7 @@ output_priunat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (priunat_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2059,7 +2166,7 @@ output_bsp_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bsp_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2093,7 +2200,7 @@ output_bspstore_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (bspstore_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2127,7 +2234,7 @@ output_rnat_psprel (offset)
      unsigned int offset;
 {
   unw_rec_list *ptr = alloc_record (rnat_psprel);
-  ptr->r.record.p.pspoff = offset / 4;
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2184,7 +2291,7 @@ output_spill_psprel (ab, reg, offset)
   unw_rec_list *ptr = alloc_record (spill_psprel);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
-  ptr->r.record.x.pspoff = offset / 4;
+  ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
   return ptr;
 }
 
@@ -2211,7 +2318,7 @@ output_spill_psprel_p (ab, reg, offset, predicate)
   unw_rec_list *ptr = alloc_record (spill_psprel_p);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
-  ptr->r.record.x.pspoff = offset / 4;
+  ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
   ptr->r.record.x.qp = predicate;
   return ptr;
 }
@@ -2275,6 +2382,10 @@ process_one_record (ptr, f)
 
   switch (ptr->r.type)
     {
+      /* This is a dummy record that takes up no space in the output.  */
+    case endp:
+      break;
+
     case gr_mem:
     case fr_mem:
     case br_mem:
@@ -2517,29 +2628,18 @@ set_imask (region, regmask, t, type)
     }
 }
 
-static int
-count_bits (unsigned long mask)
-{
-  int n = 0;
-
-  while (mask)
-    {
-      mask &= mask - 1;
-      ++n;
-    }
-  return n;
-}
-
 /* Return the number of instruction slots from FIRST_ADDR to SLOT_ADDR.
    SLOT_FRAG is the frag containing SLOT_ADDR, and FIRST_FRAG is the frag
-   containing FIRST_ADDR.  */
+   containing FIRST_ADDR.  If BEFORE_RELAX, then we use worst-case estimates
+   for frag sizes.  */
 
 unsigned long
-slot_index (slot_addr, slot_frag, first_addr, first_frag)
+slot_index (slot_addr, slot_frag, first_addr, first_frag, before_relax)
      unsigned long slot_addr;
      fragS *slot_frag;
      unsigned long first_addr;
      fragS *first_frag;
+     int before_relax;
 {
   unsigned long index = 0;
 
@@ -2554,6 +2654,46 @@ slot_index (slot_addr, slot_frag, first_addr, first_frag)
     {
       unsigned long start_addr = (unsigned long) &first_frag->fr_literal;
 
+      if (! before_relax)
+       {
+         /* We can get the final addresses only during and after
+            relaxation.  */
+         if (first_frag->fr_next && first_frag->fr_next->fr_address)
+           index += 3 * ((first_frag->fr_next->fr_address
+                          - first_frag->fr_address
+                            - first_frag->fr_fix) >> 4);
+       }
+      else
+       /* We don't know what the final addresses will be. We try our
+          best to estimate.  */
+       switch (first_frag->fr_type)
+         {
+         default:
+           break;
+
+         case rs_space:
+           as_fatal ("only constant space allocation is supported");
+           break;
+
+         case rs_align:
+         case rs_align_code:
+         case rs_align_test:
+           /* Take alignment into account.  Assume the worst case
+              before relaxation.  */
+           index += 3 * ((1 << first_frag->fr_offset) >> 4);
+           break;
+
+         case rs_org:
+           if (first_frag->fr_symbol)
+             {
+               as_fatal ("only constant offsets are supported");
+               break;
+             }
+         case rs_fill:
+           index += 3 * (first_frag->fr_offset >> 4);
+           break;
+         }
+
       /* Add in the full size of the frag converted to instruction slots.  */
       index += 3 * (first_frag->fr_fix >> 4);
       /* Subtract away the initial part before first_addr.  */
@@ -2583,8 +2723,8 @@ optimize_unw_records (list)
   /* If the only unwind record is ".prologue" or ".prologue" followed
      by ".body", then we can optimize the unwind directives away.  */
   if (list->r.type == prologue
-      && (list->next == NULL
-         || (list->next->r.type == body && list->next->next == NULL)))
+      && (list->next->r.type == endp
+         || (list->next->r.type == body && list->next->next->r.type == endp)))
     return NULL;
 
   return list;
@@ -2592,12 +2732,13 @@ optimize_unw_records (list)
 
 /* Given a complete record list, process any records which have
    unresolved fields, (ie length counts for a prologue).  After
-   this has been run, all neccessary information should be available
+   this has been run, all necessary information should be available
    within each record to generate an image.  */
 
 static void
-fixup_unw_records (list)
+fixup_unw_records (list, before_relax)
      unw_rec_list *list;
+     int before_relax;
 {
   unw_rec_list *ptr, *region = 0;
   unsigned long first_addr = 0, rlen = 0, t;
@@ -2608,7 +2749,7 @@ fixup_unw_records (list)
       if (ptr->slot_number == SLOT_NUM_NOT_SET)
        as_bad (" Insn slot not set in unwind record.");
       t = slot_index (ptr->slot_number, ptr->slot_frag,
-                     first_addr, first_frag);
+                     first_addr, first_frag, before_relax);
       switch (ptr->r.type)
        {
        case prologue:
@@ -2616,66 +2757,40 @@ fixup_unw_records (list)
        case body:
          {
            unw_rec_list *last;
-           int size, dir_len = 0;
-           unsigned long last_addr;
-           fragS *last_frag;
+           int size;
+           unsigned long last_addr = 0;
+           fragS *last_frag = NULL;
 
            first_addr = ptr->slot_number;
            first_frag = ptr->slot_frag;
-           ptr->slot_number = 0;
            /* Find either the next body/prologue start, or the end of
-              the list, and determine the size of the region.  */
-           last_addr = unwind.next_slot_number;
-           last_frag = unwind.next_slot_frag;
+              the function, and determine the size of the region.  */
            for (last = ptr->next; last != NULL; last = last->next)
              if (last->r.type == prologue || last->r.type == prologue_gr
-                 || last->r.type == body)
+                 || last->r.type == body || last->r.type == endp)
                {
                  last_addr = last->slot_number;
                  last_frag = last->slot_frag;
                  break;
                }
-             else if (!last->next)
-               {
-                 /* In the absence of an explicit .body directive,
-                    the prologue ends after the last instruction
-                    covered by an unwind directive.  */
-                 if (ptr->r.type != body)
-                   {
-                     last_addr = last->slot_number;
-                     last_frag = last->slot_frag;
-                     switch (last->r.type)
-                       {
-                       case frgr_mem:
-                         dir_len = (count_bits (last->r.record.p.frmask)
-                                    + count_bits (last->r.record.p.grmask));
-                         break;
-                       case fr_mem:
-                       case gr_mem:
-                         dir_len += count_bits (last->r.record.p.rmask);
-                         break;
-                       case br_mem:
-                       case br_gr:
-                         dir_len += count_bits (last->r.record.p.brmask);
-                         break;
-                       case gr_gr:
-                         dir_len += count_bits (last->r.record.p.grmask);
-                         break;
-                       default:
-                         dir_len = 1;
-                         break;
-                       }
-                   }
-                 break;
-               }
-           size = (slot_index (last_addr, last_frag, first_addr, first_frag)
-                   + dir_len);
+           size = slot_index (last_addr, last_frag, first_addr, first_frag,
+                              before_relax);
            rlen = ptr->r.record.r.rlen = size;
-           region = ptr;
+           if (ptr->r.type == body)
+             /* End of region.  */
+             region = 0;
+           else
+             region = ptr;
            break;
          }
        case epilogue:
-         ptr->r.record.b.t = rlen - 1 - t;
+         if (t < rlen)
+           ptr->r.record.b.t = rlen - 1 - t;
+         else
+           /* This happens when a memory-stack-less procedure uses a
+              ".restore sp" directive at the end of a region to pop
+              the frame state.  */
+           ptr->r.record.b.t = 0;
          break;
 
        case mem_stack_f:
@@ -2706,7 +2821,7 @@ fixup_unw_records (list)
        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;
@@ -2717,7 +2832,7 @@ fixup_unw_records (list)
        case fr_mem:
          if (!region)
            {
-             as_bad ("fr_mem record before region record!\n");
+             as_bad ("fr_mem record before region record!");
              return;
            }
          region->r.record.r.mask.fr_mem |= ptr->r.record.p.rmask;
@@ -2726,7 +2841,7 @@ fixup_unw_records (list)
        case gr_mem:
          if (!region)
            {
-             as_bad ("gr_mem record before region record!\n");
+             as_bad ("gr_mem record before region record!");
              return;
            }
          region->r.record.r.mask.gr_mem |= ptr->r.record.p.rmask;
@@ -2735,7 +2850,7 @@ fixup_unw_records (list)
        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;
@@ -2745,7 +2860,7 @@ fixup_unw_records (list)
        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);
@@ -2753,7 +2868,7 @@ fixup_unw_records (list)
        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);
@@ -2765,67 +2880,98 @@ fixup_unw_records (list)
     }
 }
 
-/* Helper routine for output_unw_records.  Emits the header for the unwind
-   info.  */
+/* Estimate the size of a frag before relaxing.  We only have one type of frag
+   to handle here, which is the unwind info frag.  */
 
-static int
-setup_unwind_header (int size, unsigned char **mem)
+int
+ia64_estimate_size_before_relax (fragS *frag,
+                                asection *segtype ATTRIBUTE_UNUSED)
 {
-  int x, extra = 0;
-
-  /* pad to pointer-size boundry.  */
-  x = size % md.pointer_size;
-  if (x != 0)
-    extra = md.pointer_size - x;
+  unw_rec_list *list;
+  int len, size, pad;
 
-  /* Add 8 for the header + a pointer for the 
-     personality offset.  */
-  *mem = xmalloc (size + extra + 8 + md.pointer_size);
+  /* ??? This code is identical to the first part of ia64_convert_frag.  */
+  list = (unw_rec_list *) frag->fr_opcode;
+  fixup_unw_records (list, 0);
 
-  /* Clear the padding area and personality.  */
-  memset (*mem + 8 + size, 0 , extra + md.pointer_size);
-  /* Initialize the header area.  */
+  len = calc_record_size (list);
+  /* pad to pointer-size boundary.  */
+  pad = len % md.pointer_size;
+  if (pad != 0)
+    len += md.pointer_size - pad;
+  /* Add 8 for the header.  */
+  size = len + 8;
+  /* Add a pointer for the personality offset.  */
+  if (frag->fr_offset)
+    size += md.pointer_size;
 
-  md_number_to_chars (*mem, (((bfd_vma) 1 << 48)     /* version */
-                            | (unwind.personality_routine
-                               ? ((bfd_vma) 3 << 32) /* U & E handler flags */
-                               : 0)
-                            | ((size + extra) / md.pointer_size)), /* length */
-                     8);
+  /* fr_var carries the max_chars that we created the fragment with.
+     We must, of course, have allocated enough memory earlier.  */
+  assert (frag->fr_var >= size);
 
-  return extra;
+  return frag->fr_fix + size;
 }
 
-/* Generate an unwind image from a record list.  Returns the number of
-   bytes in the resulting image. The memory image itselof is returned
-   in the 'ptr' parameter.  */
-static int
-output_unw_records (list, ptr)
-     unw_rec_list *list;
-     void **ptr;
+/* This function converts a rs_machine_dependent variant frag into a
+  normal fill frag with the unwind image from the the record list.  */
+void
+ia64_convert_frag (fragS *frag)
 {
-  int size, extra;
-  unsigned char *mem;
-
-  *ptr = NULL;
-
-  list = optimize_unw_records (list);
-  fixup_unw_records (list);
-  size = calc_record_size (list);
+  unw_rec_list *list;
+  int len, size, pad;
+  valueT flag_value;
+
+  /* ??? This code is identical to ia64_estimate_size_before_relax.  */
+  list = (unw_rec_list *) frag->fr_opcode;
+  fixup_unw_records (list, 0);
+
+  len = calc_record_size (list);
+  /* pad to pointer-size boundary.  */
+  pad = len % md.pointer_size;
+  if (pad != 0)
+    len += md.pointer_size - pad;
+  /* 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.  */
+  assert (frag->fr_var >= size);
+
+  /* Initialize the header area. fr_offset is initialized with
+     unwind.personality_routine.  */
+  if (frag->fr_offset)
+    {
+      if (md.flags & EF_IA_64_ABI64)
+       flag_value = (bfd_vma) 3 << 32;
+      else
+       /* 32-bit unwind info block.  */
+       flag_value = (bfd_vma) 0x1003 << 32;
+    }
+  else
+    flag_value = 0;
 
-  if (size > 0 || unwind.force_unwind_entry)
-    {
-      unwind.force_unwind_entry = 0;
-      extra = setup_unwind_header (size, &mem);
+ md_number_to_chars (frag->fr_literal,
+                    (((bfd_vma) 1 << 48) /* Version.  */
+                     | flag_value        /* U & E handler flags.  */
+                     | (len / md.pointer_size)), /* Length.  */
+                    8);
 
-      vbyte_mem_ptr = mem + 8;
-      process_unw_records (list, output_vbyte_mem);
+  /* Skip the header.  */
+  vbyte_mem_ptr = frag->fr_literal + 8;
+  process_unw_records (list, output_vbyte_mem);
 
-      *ptr = mem;
+  /* Fill the padding bytes with zeros.  */
+  if (pad != 0)
+    md_number_to_chars (frag->fr_literal + len + 8 - md.pointer_size + pad, 0,
+                       md.pointer_size - pad);
 
-      size += extra + 8 + md.pointer_size;
-    }
-  return size;
+  frag->fr_fix += size;
+  frag->fr_type = rs_fill;
+  frag->fr_var = 0;
+  frag->fr_offset = 0;
 }
 
 static int
@@ -2913,6 +3059,14 @@ convert_expr_to_xy_reg (e, xy, regp)
   return 1;
 }
 
+static void
+dot_align (int arg)
+{
+  /* The current frag is an alignment frag.  */
+  align_frag = frag_now;
+  s_align_bytes (arg);
+}
+
 static void
 dot_radix (dummy)
      int dummy ATTRIBUTE_UNUSED;
@@ -2930,6 +3084,17 @@ dot_radix (dummy)
     }
 }
 
+/* Helper function for .loc directives.  If the assembler is not generating
+   line number info, then we need to remember which instructions have a .loc
+   directive, and only call dwarf2_gen_line_info for those instructions.  */
+
+static void
+dot_loc (int x)
+{
+  CURR_SLOT.loc_directive_seen = 1;
+  dwarf2_directive_loc (x);
+}
+
 /* .sbss, .bss etc. are macros that expand into ".section SECNAME".  */
 static void
 dot_special_section (which)
@@ -2938,6 +3103,84 @@ dot_special_section (which)
   set_section ((char *) special_section_name[which]);
 }
 
+/* Return -1 for warning and 0 for error.  */
+
+static int
+unwind_diagnostic (const char * region, const char *directive)
+{
+  if (md.unwind_check == unwind_check_warning)
+    {
+      as_warn (".%s outside of %s", directive, region);
+      return -1;
+    }
+  else
+    {
+      as_bad (".%s outside of %s", directive, region);
+      ignore_rest_of_line ();
+      return 0;
+    }
+}
+
+/* Return 1 if a directive is in a procedure, -1 if a directive isn't in
+   a procedure but the unwind directive check is set to warning, 0 if
+   a directive isn't in a procedure and the unwind directive check is set
+   to error.  */
+
+static int
+in_procedure (const char *directive)
+{
+  if (unwind.proc_start
+      && (!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)
+    {
+      /* We are in a procedure. Check if we are in a prologue.  */
+      if (unwind.prologue)
+       return 1;
+      /* We only want to issue one message.  */
+      if (in == 1)
+       return unwind_diagnostic ("prologue", directive);
+      else
+       return -1;
+    }
+  return 0;
+}
+
+/* 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)
+    {
+      /* We are in a procedure. Check if we are in a body.  */
+      if (unwind.body)
+       return 1;
+      /* We only want to issue one message.  */
+      if (in == 1)
+       return unwind_diagnostic ("body region", directive);
+      else
+       return -1;
+    }
+  return 0;
+}
+
 static void
 add_unwind_entry (ptr)
      unw_rec_list *ptr;
@@ -2959,6 +3202,9 @@ dot_fframe (dummy)
 {
   expressionS e;
 
+  if (!in_prologue ("fframe"))
+    return;
+
   parse_operand (&e);
 
   if (e.X_op != O_constant)
@@ -2974,6 +3220,9 @@ dot_vframe (dummy)
   expressionS e;
   unsigned reg;
 
+  if (!in_prologue ("vframe"))
+    return;
+
   parse_operand (&e);
   reg = e.X_add_number - REG_GR;
   if (e.X_op == O_register && reg < 128)
@@ -2992,6 +3241,9 @@ dot_vframesp (dummy)
 {
   expressionS e;
 
+  if (!in_prologue ("vframesp"))
+    return;
+
   parse_operand (&e);
   if (e.X_op == O_constant)
     {
@@ -2999,7 +3251,7 @@ dot_vframesp (dummy)
       add_unwind_entry (output_psp_sprel (e.X_add_number));
     }
   else
-    as_bad ("First operand to .vframesp must be a general register");
+    as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
 }
 
 static void
@@ -3008,6 +3260,9 @@ dot_vframepsp (dummy)
 {
   expressionS e;
 
+  if (!in_prologue ("vframepsp"))
+    return;
+
   parse_operand (&e);
   if (e.X_op == O_constant)
     {
@@ -3015,7 +3270,7 @@ dot_vframepsp (dummy)
       add_unwind_entry (output_psp_sprel (e.X_add_number));
     }
   else
-    as_bad ("First operand to .vframepsp must be a general register");
+    as_bad ("Operand to .vframepsp must be a constant (psp-relative offset)");
 }
 
 static void
@@ -3026,6 +3281,9 @@ dot_save (dummy)
   int sep;
   int reg1, reg2;
 
+  if (!in_prologue ("save"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     as_bad ("No second operand to .save");
@@ -3103,6 +3361,9 @@ dot_restore (dummy)
   unsigned long ecount;        /* # of _additional_ regions to pop */
   int sep;
 
+  if (!in_body ("restore"))
+    return;
+
   sep = parse_operand (&e1);
   if (e1.X_op != O_register || e1.X_add_number != REG_GR + 12)
     {
@@ -3122,6 +3383,14 @@ dot_restore (dummy)
     }
   else
     ecount = unwind.prologue_count - 1;
+
+  if (ecount >= unwind.prologue_count)
+    {
+      as_bad ("Epilogue count of %lu exceeds number of nested prologues (%u)",
+             ecount + 1, unwind.prologue_count);
+      return;
+    }
+
   add_unwind_entry (output_epilogue (ecount));
 
   if (ecount < unwind.prologue_count)
@@ -3137,6 +3406,9 @@ dot_restorereg (dummy)
   unsigned int ab, reg;
   expressionS e;
 
+  if (!in_procedure ("restorereg"))
+    return;
+
   parse_operand (&e);
 
   if (!convert_expr_to_ab_reg (&e, &ab, &reg))
@@ -3155,6 +3427,9 @@ dot_restorereg_p (dummy)
   expressionS e1, e2;
   int sep;
 
+  if (!in_procedure ("restorereg.p"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
@@ -3179,80 +3454,213 @@ dot_restorereg_p (dummy)
   add_unwind_entry (output_spill_reg_p (ab, reg, 0, 0, qp));
 }
 
-static int
-generate_unwind_image (text_name)
-     const char *text_name;
-{
-  int size;
-  unsigned char *unw_rec;
-
-  /* Force out pending instructions, to make sure all unwind records have
-     a valid slot_number field.  */
-  ia64_flush_insns ();
+static char *special_linkonce_name[] =
+  {
+    ".gnu.linkonce.ia64unw.", ".gnu.linkonce.ia64unwi."
+  };
 
-  /* Generate the unwind record.  */
-  size = output_unw_records (unwind.list, (void **) &unw_rec);
-  if (size % md.pointer_size != 0)
-    as_bad ("Unwind record is not a multiple of %d bytes.", md.pointer_size);
-                      
-  /* If there are unwind records, switch sections, and output the info.  */
-  if (size != 0)
-    {
-      unsigned char *where;
-      char *sec_name;
-      expressionS exp;
+static void
+start_unwind_section (const segT text_seg, int sec_index)
+{
+  /*
+    Use a slightly ugly scheme to derive the unwind section names from
+    the text section name:
 
-      make_unw_section_name (SPECIAL_SECTION_UNWIND_INFO, text_name, sec_name);
-      set_section (sec_name);
-      bfd_set_section_flags (stdoutput, now_seg,
-                            SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+    text sect.  unwind table sect.
+    name:       name:                      comments:
+    ----------  -----------------          --------------------------------
+    .text       .IA_64.unwind
+    .text.foo   .IA_64.unwind.text.foo
+    .foo        .IA_64.unwind.foo
+    .gnu.linkonce.t.foo
+               .gnu.linkonce.ia64unw.foo
+    _info       .IA_64.unwind_info         gas issues error message (ditto)
+    _infoFOO    .IA_64.unwind_infoFOO      gas issues error message (ditto)
 
-      /* Make sure the section has 4 byte alignment for ILP32 and
-        8 byte alignment for LP64.  */
-      frag_align (md.pointer_size_shift, 0, 0);
-      record_alignment (now_seg, md.pointer_size_shift);
+    This mapping is done so that:
 
-      /* Set expression which points to start of unwind descriptor area.  */
-      unwind.info = expr_build_dot ();
+       (a) An object file with unwind info only in .text will use
+           unwind section names .IA_64.unwind and .IA_64.unwind_info.
+           This follows the letter of the ABI and also ensures backwards
+           compatibility with older toolchains.
 
-      where = (unsigned char *) frag_more (size);
+       (b) An object file with unwind info in multiple text sections
+           will use separate unwind sections for each text section.
+           This allows us to properly set the "sh_info" and "sh_link"
+           fields in SHT_IA_64_UNWIND as required by the ABI and also
+           lets GNU ld support programs with multiple segments
+           containing unwind info (as might be the case for certain
+           embedded applications).
 
-      /* Issue a label for this address, and keep track of it to put it
-        in the unwind section.  */
+       (c) An error is issued if there would be a name clash.
+  */
 
-      /* Copy the information from the unwind record into this section. The
-        data is already in the correct byte order.  */
-      memcpy (where, unw_rec, size);
+  const char *text_name, *sec_text_name;
+  char *sec_name;
+  const char *prefix = special_section_name [sec_index];
+  const char *suffix;
+  size_t prefix_len, suffix_len, sec_name_len;
 
-      /* Add the personality address to the image.  */
-      if (unwind.personality_routine != 0)
+  sec_text_name = segment_name (text_seg);
+  text_name = sec_text_name;
+  if (strncmp (text_name, "_info", 5) == 0)
+    {
+      as_bad ("Illegal section name `%s' (causes unwind section name clash)",
+             text_name);
+      ignore_rest_of_line ();
+      return;
+    }
+  if (strcmp (text_name, ".text") == 0)
+    text_name = "";
+
+  /* Build the unwind section name by appending the (possibly stripped)
+     text section name to the unwind prefix.  */
+  suffix = text_name;
+  if (strncmp (text_name, ".gnu.linkonce.t.",
+              sizeof (".gnu.linkonce.t.") - 1) == 0)
+    {
+      prefix = special_linkonce_name [sec_index - SPECIAL_SECTION_UNWIND];
+      suffix += sizeof (".gnu.linkonce.t.") - 1;
+    }
+
+  prefix_len = strlen (prefix);
+  suffix_len = strlen (suffix);
+  sec_name_len = prefix_len + suffix_len;
+  sec_name = alloca (sec_name_len + 1);
+  memcpy (sec_name, prefix, prefix_len);
+  memcpy (sec_name + prefix_len, suffix, suffix_len);
+  sec_name [sec_name_len] = '\0';
+
+  /* Handle COMDAT group.  */
+  if (suffix == text_name && (text_seg->flags & SEC_LINK_ONCE) != 0)
+    {
+      char *section;
+      size_t len, group_name_len;
+      const char *group_name = elf_group_name (text_seg);
+
+      if (group_name == NULL)
+       {
+         as_bad ("Group section `%s' has no group signature",
+                 sec_text_name);
+         ignore_rest_of_line ();
+         return;
+       }
+      /* We have to construct a fake section directive. */
+      group_name_len = strlen (group_name);
+      len = (sec_name_len
+            + 16                       /* ,"aG",@progbits,  */
+            + group_name_len           /* ,group_name  */
+            + 7);                      /* ,comdat  */
+
+      section = alloca (len + 1);
+      memcpy (section, sec_name, sec_name_len);
+      memcpy (section + sec_name_len, ",\"aG\",@progbits,", 16);
+      memcpy (section + sec_name_len + 16, group_name, group_name_len);
+      memcpy (section + len - 7, ",comdat", 7);
+      section [len] = '\0';
+      set_section (section);
+    }
+  else
+    {
+      set_section (sec_name);
+      bfd_set_section_flags (stdoutput, now_seg,
+                            SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+    }
+
+  elf_linked_to_section (now_seg) = text_seg;
+}
+
+static void
+generate_unwind_image (const segT text_seg)
+{
+  int size, pad;
+  unw_rec_list *list;
+
+  /* Mark the end of the unwind info, so that we can compute the size of the
+     last unwind region.  */
+  add_unwind_entry (output_endp ());
+
+  /* Force out pending instructions, to make sure all unwind records have
+     a valid slot_number field.  */
+  ia64_flush_insns ();
+
+  /* Generate the unwind record.  */
+  list = optimize_unw_records (unwind.list);
+  fixup_unw_records (list, 1);
+  size = calc_record_size (list);
+
+  if (size > 0 || unwind.force_unwind_entry)
+    {
+      unwind.force_unwind_entry = 0;
+      /* pad to pointer-size boundary.  */
+      pad = size % md.pointer_size;
+      if (pad != 0)
+       size += md.pointer_size - pad;
+      /* 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 (size != 0)
+    {
+      expressionS exp;
+      bfd_reloc_code_real_type reloc;
+
+      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.  */
+      frag_align (md.pointer_size_shift, 0, 0);
+      record_alignment (now_seg, md.pointer_size_shift);
+
+      /* Set expression which points to start of unwind descriptor area.  */
+      unwind.info = expr_build_dot ();
+      
+      frag_var (rs_machine_dependent, size, size, 0, 0,
+               (offsetT) (long) unwind.personality_routine,
+               (char *) list);
+
+      /* Add the personality address to the image.  */
+      if (unwind.personality_routine != 0)
        {
-         exp.X_op  = O_symbol;
+         exp.X_op = O_symbol;
          exp.X_add_symbol = unwind.personality_routine;
          exp.X_add_number = 0;
-         fix_new_exp (frag_now, frag_now_fix () - 8, 8,
-                            &exp, 0, BFD_RELOC_IA64_LTOFF_FPTR64LSB);
+
+         if (md.flags & EF_IA_64_BE)
+           {
+             if (md.flags & EF_IA_64_ABI64)
+               reloc = BFD_RELOC_IA64_LTOFF_FPTR64MSB;
+             else
+               reloc = BFD_RELOC_IA64_LTOFF_FPTR32MSB;
+           }
+         else
+           {
+             if (md.flags & EF_IA_64_ABI64)
+               reloc = BFD_RELOC_IA64_LTOFF_FPTR64LSB;
+             else
+               reloc = BFD_RELOC_IA64_LTOFF_FPTR32LSB;
+           }
+
+         fix_new_exp (frag_now, frag_now_fix () - md.pointer_size,
+                      md.pointer_size, &exp, 0, reloc);
          unwind.personality_routine = 0;
        }
     }
 
-  free_list_records (unwind.list);
+  free_saved_prologue_counts ();
   unwind.list = unwind.tail = unwind.current_entry = NULL;
-
-  return size;
 }
 
 static void
 dot_handlerdata (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  const char *text_name = segment_name (now_seg);
-
-  /* If text section name starts with ".text" (which it should),
-     strip this prefix off.  */
-  if (strcmp (text_name, ".text") == 0)
-    text_name = "";
-
+  if (!in_procedure ("handlerdata"))
+    return;
   unwind.force_unwind_entry = 1;
 
   /* Remember which segment we're in so we can switch back after .endp */
@@ -3262,7 +3670,7 @@ dot_handlerdata (dummy)
   /* Generate unwind info into unwind-info section and then leave that
      section as the currently active one so dataXX directives go into
      the language specific data area of the unwind info block.  */
-  generate_unwind_image (text_name);
+  generate_unwind_image (now_seg);
   demand_empty_rest_of_line ();
 }
 
@@ -3270,6 +3678,8 @@ static void
 dot_unwentry (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("unwentry"))
+    return;
   unwind.force_unwind_entry = 1;
   demand_empty_rest_of_line ();
 }
@@ -3281,6 +3691,9 @@ dot_altrp (dummy)
   expressionS e;
   unsigned reg;
 
+  if (!in_prologue ("altrp"))
+    return;
+
   parse_operand (&e);
   reg = e.X_add_number - REG_BR;
   if (e.X_op == O_register && reg < 8)
@@ -3297,6 +3710,9 @@ dot_savemem (psprel)
   int sep;
   int reg1, val;
 
+  if (!in_prologue (psprel ? "savepsp" : "savesp"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     as_bad ("No second operand to .save%ssp", psprel ? "p" : "");
@@ -3389,6 +3805,10 @@ dot_saveg (dummy)
 {
   expressionS e1, e2;
   int sep;
+
+  if (!in_prologue ("save.g"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep == ',')
     parse_operand (&e2);
@@ -3417,6 +3837,10 @@ dot_savef (dummy)
 {
   expressionS e1;
   int sep;
+
+  if (!in_prologue ("save.f"))
+    return;
+
   sep = parse_operand (&e1);
 
   if (e1.X_op != O_constant)
@@ -3434,6 +3858,9 @@ dot_saveb (dummy)
   unsigned char sep;
   int brmask;
 
+  if (!in_prologue ("save.b"))
+    return;
+
   sep = parse_operand (&e1);
   if (e1.X_op != O_constant)
     {
@@ -3457,7 +3884,7 @@ dot_saveb (dummy)
     add_unwind_entry (output_br_mem (brmask));
 
   if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    ignore_rest_of_line ();
+    demand_empty_rest_of_line ();
 }
 
 static void
@@ -3466,6 +3893,10 @@ dot_savegf (dummy)
 {
   expressionS e1, e2;
   int sep;
+
+  if (!in_prologue ("save.gf"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep == ',')
     parse_operand (&e2);
@@ -3487,9 +3918,12 @@ dot_spill (dummy)
   expressionS e;
   unsigned char sep;
 
+  if (!in_prologue ("spill"))
+    return;
+
   sep = parse_operand (&e);
   if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    ignore_rest_of_line ();
+    demand_empty_rest_of_line ();
 
   if (e.X_op != O_constant)
     as_bad ("Operand to .spill must be a constant");
@@ -3501,9 +3935,13 @@ static void
 dot_spillreg (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  int sep, ab, xy, reg, treg;
+  int sep;
+  unsigned int ab, xy, reg, treg;
   expressionS e1, e2;
 
+  if (!in_procedure ("spillreg"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
@@ -3533,7 +3971,11 @@ dot_spillmem (psprel)
      int psprel;
 {
   expressionS e1, e2;
-  int sep, ab, reg;
+  int sep;
+  unsigned int ab, reg;
+
+  if (!in_procedure ("spillmem"))
+    return;
 
   sep = parse_operand (&e1);
   if (sep != ',')
@@ -3568,10 +4010,14 @@ static void
 dot_spillreg_p (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  int sep, ab, xy, reg, treg;
+  int sep;
+  unsigned int ab, xy, reg, treg;
   expressionS e1, e2, e3;
   unsigned int qp;
 
+  if (!in_procedure ("spillreg.p"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
@@ -3616,9 +4062,13 @@ dot_spillmem_p (psprel)
      int psprel;
 {
   expressionS e1, e2, e3;
-  int sep, ab, reg;
+  int sep;
+  unsigned int ab, reg;
   unsigned int qp;
 
+  if (!in_procedure ("spillmem.p"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
@@ -3663,12 +4113,70 @@ dot_spillmem_p (psprel)
     add_unwind_entry (output_spill_sprel_p (ab, reg, e3.X_add_number, qp));
 }
 
+static unsigned int
+get_saved_prologue_count (lbl)
+     unsigned long lbl;
+{
+  label_prologue_count *lpc = unwind.saved_prologue_counts;
+
+  while (lpc != NULL && lpc->label_number != lbl)
+    lpc = lpc->next;
+
+  if (lpc != NULL)
+    return lpc->prologue_count;
+
+  as_bad ("Missing .label_state %ld", lbl);
+  return 1;
+}
+
+static void
+save_prologue_count (lbl, count)
+     unsigned long lbl;
+     unsigned int count;
+{
+  label_prologue_count *lpc = unwind.saved_prologue_counts;
+
+  while (lpc != NULL && lpc->label_number != lbl)
+    lpc = lpc->next;
+
+  if (lpc != NULL)
+    lpc->prologue_count = count;
+  else
+    {
+      label_prologue_count *new_lpc = xmalloc (sizeof (* new_lpc));
+
+      new_lpc->next = unwind.saved_prologue_counts;
+      new_lpc->label_number = lbl;
+      new_lpc->prologue_count = count;
+      unwind.saved_prologue_counts = new_lpc;
+    }
+}
+
+static void
+free_saved_prologue_counts ()
+{
+  label_prologue_count *lpc = unwind.saved_prologue_counts;
+  label_prologue_count *next;
+
+  while (lpc != NULL)
+    {
+      next = lpc->next;
+      free (lpc);
+      lpc = next;
+    }
+
+  unwind.saved_prologue_counts = NULL;
+}
+
 static void
 dot_label_state (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
 
+  if (!in_body ("label_state"))
+    return;
+
   parse_operand (&e);
   if (e.X_op != O_constant)
     {
@@ -3676,6 +4184,7 @@ dot_label_state (dummy)
       return;
     }
   add_unwind_entry (output_label_state (e.X_add_number));
+  save_prologue_count (e.X_add_number, unwind.prologue_count);
 }
 
 static void
@@ -3684,6 +4193,9 @@ dot_copy_state (dummy)
 {
   expressionS e;
 
+  if (!in_body ("copy_state"))
+    return;
+
   parse_operand (&e);
   if (e.X_op != O_constant)
     {
@@ -3691,6 +4203,7 @@ dot_copy_state (dummy)
       return;
     }
   add_unwind_entry (output_copy_state (e.X_add_number));
+  unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
 }
 
 static void
@@ -3700,6 +4213,9 @@ dot_unwabi (dummy)
   expressionS e1, e2;
   unsigned char sep;
 
+  if (!in_procedure ("unwabi"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
@@ -3708,7 +4224,7 @@ dot_unwabi (dummy)
     }
   sep = parse_operand (&e2);
   if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    ignore_rest_of_line ();
+    demand_empty_rest_of_line ();
 
   if (e1.X_op != O_constant)
     {
@@ -3730,6 +4246,8 @@ dot_personality (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   char *name, *p, c;
+  if (!in_procedure ("personality"))
+    return;
   SKIP_WHITESPACE ();
   name = input_line_pointer;
   c = get_symbol_end ();
@@ -3748,7 +4266,7 @@ dot_proc (dummy)
   char *name, *p, c;
   symbolS *sym;
 
-  unwind.proc_start = expr_build_dot ();
+  unwind.proc_start = 0;
   /* Parse names of main and alternate entry points and mark them as
      function symbols:  */
   while (1)
@@ -3757,22 +4275,34 @@ dot_proc (dummy)
       name = input_line_pointer;
       c = get_symbol_end ();
       p = input_line_pointer;
-      sym = symbol_find_or_make (name);
-      if (unwind.proc_start == 0)
+      if (!*name)
+       as_bad ("Empty argument of .proc");
+      else
        {
-         unwind.proc_start = sym;
+         sym = symbol_find_or_make (name);
+         if (S_IS_DEFINED (sym))
+           as_bad ("`%s' was already defined", name);
+         else if (unwind.proc_start == 0)
+           {
+             unwind.proc_start = sym;
+           }
+         symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
        }
-      symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
       *p = c;
       SKIP_WHITESPACE ();
       if (*input_line_pointer != ',')
        break;
       ++input_line_pointer;
     }
+  if (unwind.proc_start == 0)
+    unwind.proc_start = expr_build_dot ();
   demand_empty_rest_of_line ();
   ia64_do_align (16);
 
+  unwind.prologue = 0;
   unwind.prologue_count = 0;
+  unwind.body = 0;
+  unwind.insn = 0;
   unwind.list = unwind.tail = unwind.current_entry = NULL;
   unwind.personality_routine = 0;
 }
@@ -3781,8 +4311,14 @@ static void
 dot_body (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("body"))
+    return;
+  if (!unwind.prologue && !unwind.body && unwind.insn)
+    as_warn ("Initial .body should precede any instructions");
+
   unwind.prologue = 0;
   unwind.prologue_mask = 0;
+  unwind.body = 1;
 
   add_unwind_entry (output_body ());
   demand_empty_rest_of_line ();
@@ -3795,6 +4331,17 @@ dot_prologue (dummy)
   unsigned char sep;
   int mask = 0, grsave = 0;
 
+  if (!in_procedure ("prologue"))
+    return;
+  if (unwind.prologue)
+    {
+      as_bad (".prologue within prologue");
+      ignore_rest_of_line ();
+      return;
+    }
+  if (!unwind.body && unwind.insn)
+    as_warn ("Initial .prologue should precede any instructions");
+
   if (!is_it_end_of_statement ())
     {
       expressionS e1, e2;
@@ -3803,7 +4350,7 @@ dot_prologue (dummy)
        as_bad ("No second operand to .prologue");
       sep = parse_operand (&e2);
       if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-       ignore_rest_of_line ();
+       demand_empty_rest_of_line ();
 
       if (e1.X_op == O_constant)
        {
@@ -3827,6 +4374,7 @@ dot_prologue (dummy)
 
   unwind.prologue = 1;
   unwind.prologue_mask = mask;
+  unwind.body = 0;
   ++unwind.prologue_count;
 }
 
@@ -3835,12 +4383,19 @@ dot_endp (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
-  unsigned char *ptr;
+  char *ptr;
   int bytes_per_address;
   long where;
   segT saved_seg;
   subsegT saved_subseg;
-  const char *sec_name, *text_name;
+  char *name, *default_name, *p, c;
+  symbolS *sym;
+  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)
     {
@@ -3854,67 +4409,20 @@ dot_endp (dummy)
       saved_subseg = now_subseg;
     }
 
-  /*
-    Use a slightly ugly scheme to derive the unwind section names from
-    the text section name:
-
-    text sect.  unwind table sect.
-    name:       name:                      comments:
-    ----------  -----------------          --------------------------------
-    .text       .IA_64.unwind
-    .text.foo   .IA_64.unwind.text.foo
-    .foo        .IA_64.unwind.foo
-    .gnu.linkonce.t.foo
-               .gnu.linkonce.ia64unw.foo
-    _info       .IA_64.unwind_info         gas issues error message (ditto)
-    _infoFOO    .IA_64.unwind_infoFOO      gas issues error message (ditto)
-
-    This mapping is done so that:
-
-       (a) An object file with unwind info only in .text will use
-           unwind section names .IA_64.unwind and .IA_64.unwind_info.
-           This follows the letter of the ABI and also ensures backwards
-           compatibility with older toolchains.
-
-       (b) An object file with unwind info in multiple text sections
-           will use separate unwind sections for each text section.
-           This allows us to properly set the "sh_info" and "sh_link"
-           fields in SHT_IA_64_UNWIND as required by the ABI and also
-           lets GNU ld support programs with multiple segments
-           containing unwind info (as might be the case for certain
-           embedded applications).
-           
-       (c) An error is issued if there would be a name clash.
-  */
-  text_name = segment_name (saved_seg);
-  if (strncmp (text_name, "_info", 5) == 0)
-    {
-      as_bad ("Illegal section name `%s' (causes unwind section name clash)",
-             text_name);
-      ignore_rest_of_line ();
-      return;
-    }
-  if (strcmp (text_name, ".text") == 0)
-    text_name = "";
-
-  expression (&e);
-  demand_empty_rest_of_line ();
-
   insn_group_break (1, 0, 0);
 
   /* If there wasn't a .handlerdata, we haven't generated an image yet.  */
   if (!unwind.info)
-    generate_unwind_image (text_name);
+    generate_unwind_image (saved_seg);
 
   if (unwind.info || unwind.force_unwind_entry)
     {
+      symbolS *proc_end;
+
       subseg_set (md.last_text_seg, 0);
-      unwind.proc_end = expr_build_dot ();
+      proc_end = expr_build_dot ();
 
-      make_unw_section_name (SPECIAL_SECTION_UNWIND, text_name, sec_name);
-      set_section ((char *) sec_name);
-      bfd_set_section_flags (stdoutput, now_seg,
-                            SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+      start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND);
 
       /* Make sure that section has 4 byte alignment for ILP32 and
          8 byte alignment for LP64.  */
@@ -3926,7 +4434,7 @@ dot_endp (dummy)
       where = frag_now_fix () - (3 * md.pointer_size);
       bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8;
 
-      /* Issue the values of  a) Proc Begin, b) Proc End, c) Unwind Record. */
+      /* Issue the values of  a) Proc Begin, b) Proc End, c) Unwind Record.  */
       e.X_op = O_pseudo_fixup;
       e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
       e.X_add_number = 0;
@@ -3936,7 +4444,7 @@ dot_endp (dummy)
       e.X_op = O_pseudo_fixup;
       e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
       e.X_add_number = 0;
-      e.X_add_symbol = unwind.proc_end;
+      e.X_add_symbol = proc_end;
       ia64_cons_fix_new (frag_now, where + bytes_per_address,
                         bytes_per_address, &e);
 
@@ -3955,7 +4463,88 @@ dot_endp (dummy)
 
     }
   subseg_set (saved_seg, saved_subseg);
-  unwind.proc_start = unwind.proc_end = unwind.info = 0;
+
+  if (unwind.proc_start)
+    default_name = (char *) S_GET_NAME (unwind.proc_start);
+  else
+    default_name = NULL;
+
+  /* Parse names of main and alternate entry points and set symbol sizes.  */
+  while (1)
+    {
+      SKIP_WHITESPACE ();
+      name = input_line_pointer;
+      c = get_symbol_end ();
+      p = input_line_pointer;
+      if (!*name)
+       {
+         if (md.unwind_check == unwind_check_warning)
+           {
+             if (default_name)
+               {
+                 as_warn ("Empty argument of .endp. Use the default name `%s'",
+                          default_name);
+                 name = default_name;
+               }
+             else
+               as_warn ("Empty argument of .endp");
+           }
+         else
+           as_bad ("Empty argument of .endp");
+       }
+      if (*name)
+       {
+         sym = symbol_find (name);
+         if (!sym
+             && md.unwind_check == unwind_check_warning
+             && default_name
+             && default_name != name)
+           {
+             /* We have a bad name. Try the default one if needed.  */
+             as_warn ("`%s' was not defined within procedure. Use the default name `%s'",
+                      name, default_name);
+             name = default_name;
+             sym = symbol_find (name);
+           }
+         if (!sym || !S_IS_DEFINED (sym))
+           as_bad ("`%s' was not defined within procedure", name);
+         else if (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
+                   {
+                     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;
+                   }
+               }
+           }
+       }
+      *p = c;
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer != ',')
+       break;
+      ++input_line_pointer;
+    }
+  demand_empty_rest_of_line ();
+  unwind.proc_start = unwind.info = 0;
 }
 
 static void
@@ -4016,6 +4605,7 @@ dot_rot (type)
   for (dr = md.dynreg[type]; dr && dr->num_regs; dr = dr->next)
     {
       hash_delete (md.dynreg_hash, dr->name);
+      /* FIXME: Free dr->name.  */
       dr->num_regs = 0;
     }
 
@@ -4024,8 +4614,8 @@ dot_rot (type)
     {
       start = input_line_pointer;
       ch = get_symbol_end ();
+      len = strlen (ia64_canonicalize_symbol_name (start));
       *input_line_pointer = ch;
-      len = (input_line_pointer - start);
 
       SKIP_WHITESPACE ();
       if (*input_line_pointer != '[')
@@ -4074,16 +4664,16 @@ dot_rot (type)
          break;
        }
 
-      name = obstack_alloc (&notes, len + 1);
-      memcpy (name, start, len);
-      name[len] = '\0';
-
       if (!*drpp)
        {
          *drpp = obstack_alloc (&notes, sizeof (*dr));
          memset (*drpp, 0, sizeof (*dr));
        }
 
+      name = obstack_alloc (&notes, len + 1);
+      memcpy (name, start, len);
+      name[len] = '\0';
+
       dr = *drpp;
       dr->name = name;
       dr->num_regs = num_regs;
@@ -4094,6 +4684,7 @@ dot_rot (type)
       if (hash_insert (md.dynreg_hash, name, dr))
        {
          as_bad ("Attempt to redefine register set `%s'", name);
+         obstack_free (&notes, name);
          goto err;
        }
 
@@ -4113,17 +4704,41 @@ static void
 dot_byteorder (byteorder)
      int byteorder;
 {
-  target_big_endian = byteorder;
-}
-
-static void
-dot_psr (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  char *option;
-  int ch;
+  segment_info_type *seginfo = seg_info (now_seg);
 
-  while (1)
+  if (byteorder == -1)
+    {
+      if (seginfo->tc_segment_info_data.endian == 0)
+       seginfo->tc_segment_info_data.endian = default_big_endian ? 1 : 2;
+      byteorder = seginfo->tc_segment_info_data.endian == 1;
+    }
+  else
+    seginfo->tc_segment_info_data.endian = byteorder ? 1 : 2;
+
+  if (target_big_endian != byteorder)
+    {
+      target_big_endian = byteorder;
+      if (target_big_endian)
+       {
+         ia64_number_to_chars = number_to_chars_bigendian;
+         ia64_float_to_chars = ia64_float_to_chars_bigendian;
+       }
+      else
+       {
+         ia64_number_to_chars = number_to_chars_littleendian;
+         ia64_float_to_chars = ia64_float_to_chars_littleendian;
+       }
+    }
+}
+
+static void
+dot_psr (dummy)
+     int dummy ATTRIBUTE_UNUSED;
+{
+  char *option;
+  int ch;
+
+  while (1)
     {
       option = input_line_pointer;
       ch = get_symbol_end ();
@@ -4149,13 +4764,6 @@ dot_psr (dummy)
   demand_empty_rest_of_line ();
 }
 
-static void
-dot_alias (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  as_bad (".alias not implemented yet");
-}
-
 static void
 dot_ln (dummy)
      int dummy ATTRIBUTE_UNUSED;
@@ -4164,49 +4772,75 @@ dot_ln (dummy)
   demand_empty_rest_of_line ();
 }
 
-static char *
-parse_section_name ()
+static void
+cross_section (ref, cons, ua)
+     int ref;
+     void (*cons) PARAMS((int));
+     int ua;
 {
-  char *name;
-  int len;
+  char *start, *end;
+  int saved_auto_align;
+  unsigned int section_count;
 
   SKIP_WHITESPACE ();
-  if (*input_line_pointer != '"')
+  start = input_line_pointer;
+  if (*start == '"')
     {
-      as_bad ("Missing section name");
-      ignore_rest_of_line ();
-      return 0;
+      int len;
+      char *name;
+
+      name = demand_copy_C_string (&len);
+      obstack_free(&notes, name);
+      if (!name)
+       {
+         ignore_rest_of_line ();
+         return;
+       }
     }
-  name = demand_copy_C_string (&len);
-  if (!name)
+  else
     {
-      ignore_rest_of_line ();
-      return 0;
+      char c = get_symbol_end ();
+
+      if (input_line_pointer == start)
+       {
+         as_bad ("Missing section name");
+         ignore_rest_of_line ();
+         return;
+       }
+      *input_line_pointer = c;
     }
+  end = input_line_pointer;
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
     {
       as_bad ("Comma expected after section name");
       ignore_rest_of_line ();
-      return 0;
+      return;
     }
-  ++input_line_pointer;                /* skip comma */
-  return name;
+  *end = '\0';
+  end = input_line_pointer + 1;                /* skip comma */
+  input_line_pointer = start;
+  md.keep_pending_output = 1;
+  section_count = bfd_count_sections(stdoutput);
+  obj_elf_section (0);
+  if (section_count != bfd_count_sections(stdoutput))
+    as_warn ("Creating sections with .xdataN/.xrealN/.xstringZ is deprecated.");
+  input_line_pointer = end;
+  saved_auto_align = md.auto_align;
+  if (ua)
+    md.auto_align = 0;
+  (*cons) (ref);
+  if (ua)
+    md.auto_align = saved_auto_align;
+  obj_elf_previous (0);
+  md.keep_pending_output = 0;
 }
 
 static void
 dot_xdata (size)
      int size;
 {
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  cons (size);
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (size, cons, 0);
 }
 
 /* Why doesn't float_cons() call md_cons_align() the way cons() does?  */
@@ -4215,19 +4849,25 @@ static void
 stmt_float_cons (kind)
      int kind;
 {
-  size_t size;
+  size_t alignment;
 
   switch (kind)
     {
-    case 'd': size = 8; break;
-    case 'x': size = 10; break;
+    case 'd':
+      alignment = 8;
+      break;
+
+    case 'x':
+    case 'X':
+      alignment = 16;
+      break;
 
     case 'f':
     default:
-      size = 4;
+      alignment = 4;
       break;
     }
-  ia64_do_align (size);
+  ia64_do_align (alignment);
   float_cons (kind);
 }
 
@@ -4246,66 +4886,28 @@ static void
 dot_xfloat_cons (kind)
      int kind;
 {
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  stmt_float_cons (kind);
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (kind, stmt_float_cons, 0);
 }
 
 static void
 dot_xstringer (zero)
      int zero;
 {
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  stringer (zero);
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (zero, stringer, 0);
 }
 
 static void
 dot_xdata_ua (size)
      int size;
 {
-  int saved_auto_align = md.auto_align;
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  md.auto_align = 0;
-  cons (size);
-  md.auto_align = saved_auto_align;
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (size, cons, 1);
 }
 
 static void
 dot_xfloat_cons_ua (kind)
      int kind;
 {
-  int saved_auto_align = md.auto_align;
-  char *name = parse_section_name ();
-  if (!name)
-    return;
-
-  md.keep_pending_output = 1;
-  set_section (name);
-  md.auto_align = 0;
-  stmt_float_cons (kind);
-  md.auto_align = saved_auto_align;
-  obj_elf_previous (0);
-  md.keep_pending_output = 0;
+  cross_section (kind, float_cons, 1);
 }
 
 /* .reg.val <regname>,value */
@@ -4331,7 +4933,7 @@ dot_reg_val (dummy)
     {
       valueT value = get_absolute_expression ();
       int regno = reg.X_add_number;
-      if (regno < REG_GR || regno > REG_GR + 128)
+      if (regno <= REG_GR || regno > REG_GR + 127)
        as_warn (_("Register value annotation ignored"));
       else
        {
@@ -4343,6 +4945,23 @@ dot_reg_val (dummy)
   demand_empty_rest_of_line ();
 }
 
+/*
+  .serialize.data
+  .serialize.instruction
+ */
+static void
+dot_serialize (type)
+     int type;
+{
+  insn_group_break (0, 0, 0);
+  if (type)
+    instruction_serialization ();
+  else
+    data_serialization ();
+  insn_group_break (0, 0, 0);
+  demand_empty_rest_of_line ();
+}
+
 /* select dv checking mode
    .auto
    .explicit
@@ -4405,9 +5024,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 [,...]]
  */
 
@@ -4421,28 +5040,43 @@ dot_pred_rel (type)
 
   if (type == 0)
     {
-      if (*input_line_pointer != '"')
-       {
-         as_bad (_("Missing predicate relation type"));
-         ignore_rest_of_line ();
-         return;
-       }
-      else
+      if (*input_line_pointer == '"')
        {
          int len;
          char *form = demand_copy_C_string (&len);
+
          if (strcmp (form, "mutex") == 0)
            type = 'm';
          else if (strcmp (form, "clear") == 0)
            type = 'c';
          else if (strcmp (form, "imply") == 0)
            type = 'i';
-         else
-           {
-             as_bad (_("Unrecognized predicate relation type"));
-             ignore_rest_of_line ();
-             return;
-           }
+         obstack_free (&notes, form);
+       }
+      else if (*input_line_pointer == '@')
+       {
+         char *form = ++input_line_pointer;
+         char c = get_symbol_end();
+
+         if (strcmp (form, "mutex") == 0)
+           type = 'm';
+         else if (strcmp (form, "clear") == 0)
+           type = 'c';
+         else if (strcmp (form, "imply") == 0)
+           type = 'i';
+         *input_line_pointer = c;
+       }
+      else
+       {
+         as_bad (_("Missing predicate relation type"));
+         ignore_rest_of_line ();
+         return;
+       }
+      if (type == 0)
+       {
+         as_bad (_("Unrecognized predicate relation type"));
+         ignore_rest_of_line ();
+         return;
        }
       if (*input_line_pointer == ',')
        ++input_line_pointer;
@@ -4452,59 +5086,57 @@ dot_pred_rel (type)
   SKIP_WHITESPACE ();
   while (1)
     {
-      valueT bit = 1;
+      valueT bits = 1;
       int regno;
-
-      if (toupper (*input_line_pointer) != 'P'
-         || (regno = atoi (++input_line_pointer)) < 0
-         || regno > 63)
-       {
-         as_bad (_("Predicate register expected"));
-         ignore_rest_of_line ();
-         return;
-       }
-      while (isdigit (*input_line_pointer))
-       ++input_line_pointer;
-      if (p1 == -1)
-       p1 = regno;
-      else if (p2 == -1)
-       p2 = regno;
-      bit <<= regno;
-      if (mask & bit)
-       as_warn (_("Duplicate predicate register ignored"));
-      mask |= bit;
-      count++;
-      /* See if it's a range.  */
-      if (*input_line_pointer == '-')
-       {
-         valueT stop = 1;
-         ++input_line_pointer;
-
-         if (toupper (*input_line_pointer) != 'P'
-             || (regno = atoi (++input_line_pointer)) < 0
-             || regno > 63)
-           {
-             as_bad (_("Predicate register expected"));
-             ignore_rest_of_line ();
-             return;
-           }
-         while (isdigit (*input_line_pointer))
-           ++input_line_pointer;
-         stop <<= regno;
-         if (bit >= stop)
+      expressionS pr, *pr1, *pr2;
+
+      expression (&pr);
+      if (pr.X_op == O_register
+         && pr.X_add_number >= REG_P
+         && pr.X_add_number <= REG_P + 63)
+       {
+         regno = pr.X_add_number - REG_P;
+         bits <<= regno;
+         count++;
+         if (p1 == -1)
+           p1 = regno;
+         else if (p2 == -1)
+           p2 = regno;
+       }
+      else if (type != 'i'
+         && pr.X_op == O_subtract
+         && (pr1 = symbol_get_value_expression (pr.X_add_symbol))
+         && pr1->X_op == O_register
+         && pr1->X_add_number >= REG_P
+         && pr1->X_add_number <= REG_P + 63
+         && (pr2 = symbol_get_value_expression (pr.X_op_symbol))
+         && pr2->X_op == O_register
+         && pr2->X_add_number >= REG_P
+         && pr2->X_add_number <= REG_P + 63)
+       {
+         /* It's a range.  */
+         int stop;
+
+         regno = pr1->X_add_number - REG_P;
+         stop = pr2->X_add_number - REG_P;
+         if (regno >= stop)
            {
              as_bad (_("Bad register range"));
              ignore_rest_of_line ();
              return;
            }
-         while (bit < stop)
-           {
-             bit <<= 1;
-             mask |= bit;
-             count++;
-           }
-         SKIP_WHITESPACE ();
+         bits = ((bits << stop) << 1) - (bits << regno);
+         count += stop - regno + 1;
+       }
+      else
+       {
+         as_bad (_("Predicate register expected"));
+         ignore_rest_of_line ();
+         return;
        }
+      if (mask & bits)
+       as_warn (_("Duplicate predicate register ignored"));
+      mask |= bits;
       if (*input_line_pointer != ',')
        break;
       ++input_line_pointer;
@@ -4624,6 +5256,7 @@ const pseudo_typeS md_pseudo_table[] =
   {
     { "radix", dot_radix, 0 },
     { "lcomm", s_lcomm_bytes, 1 },
+    { "loc", dot_loc, 0 },
     { "bss", dot_special_section, SPECIAL_SECTION_BSS },
     { "sbss", dot_special_section, SPECIAL_SECTION_SBSS },
     { "sdata", dot_special_section, SPECIAL_SECTION_SDATA },
@@ -4637,8 +5270,6 @@ const pseudo_typeS md_pseudo_table[] =
     { "body", dot_body, 0 },
     { "prologue", dot_prologue, 0 },
     { "endp", dot_endp, 0 },
-    { "file", dwarf2_directive_file, 0 },
-    { "loc", dwarf2_directive_loc, 0 },
 
     { "fframe", dot_fframe, 0 },
     { "vframe", dot_vframe, 0 },
@@ -4668,9 +5299,6 @@ const pseudo_typeS md_pseudo_table[] =
     { "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 },
@@ -4682,11 +5310,7 @@ const pseudo_typeS md_pseudo_table[] =
     { "bbb", dot_template, 0xb },
     { "mmb", dot_template, 0xc },
     { "mfb", dot_template, 0xe },
-#if 0
-    { "lb", dot_scope, 0 },
-    { "le", dot_scope, 1 },
-#endif
-    { "align", s_align_bytes, 0 },
+    { "align", dot_align, 0 },
     { "regstk", dot_regstk, 0 },
     { "rotr", dot_rot, DYNREG_GR },
     { "rotf", dot_rot, DYNREG_FR },
@@ -4695,15 +5319,18 @@ const pseudo_typeS md_pseudo_table[] =
     { "msb", dot_byteorder, 1 },
     { "psr", dot_psr, 0 },
     { "alias", dot_alias, 0 },
+    { "secalias", dot_alias, 1 },
     { "ln", dot_ln, 0 },               /* source line info (for debugging) */
 
     { "xdata1", dot_xdata, 1 },
     { "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' },
     { "xstring", dot_xstringer, 0 },
     { "xstringz", dot_xstringer, 1 },
 
@@ -4711,9 +5338,11 @@ const pseudo_typeS md_pseudo_table[] =
     { "xdata2.ua", dot_xdata_ua, 2 },
     { "xdata4.ua", dot_xdata_ua, 4 },
     { "xdata8.ua", dot_xdata_ua, 8 },
+    { "xdata16.ua", dot_xdata_ua, 16 },
     { "xreal4.ua", dot_xfloat_cons_ua, 'f' },
     { "xreal8.ua", dot_xfloat_cons_ua, 'd' },
     { "xreal10.ua", dot_xfloat_cons_ua, 'x' },
+    { "xreal16.ua", dot_xfloat_cons_ua, 'X' },
 
     /* annotations/DV checking support */
     { "entry", dot_entry, 0 },
@@ -4724,6 +5353,8 @@ const pseudo_typeS md_pseudo_table[] =
     { "pred.rel.mutex", dot_pred_rel, 'm' },
     { "pred.safe_across_calls", dot_pred_rel, 's' },
     { "reg.val", dot_reg_val, 0 },
+    { "serialize.data", dot_serialize, 0 },
+    { "serialize.instruction", dot_serialize, 1 },
     { "auto", dot_dv_mode, 'a' },
     { "explicit", dot_dv_mode, 'e' },
     { "default", dot_dv_mode, 'd' },
@@ -4753,9 +5384,11 @@ pseudo_opcode[] =
     { "data2", cons, 2 },
     { "data4", cons, 4 },
     { "data8", cons, 8 },
+    { "data16", cons, 16 },
     { "real4", stmt_float_cons, 'f' },
     { "real8", stmt_float_cons, 'd' },
     { "real10", stmt_float_cons, 'x' },
+    { "real16", stmt_float_cons, 'X' },
     { "string", stringer, 0 },
     { "stringz", stringer, 1 },
 
@@ -4763,9 +5396,11 @@ pseudo_opcode[] =
     { "data2.ua", stmt_cons_ua, 2 },
     { "data4.ua", stmt_cons_ua, 4 },
     { "data8.ua", stmt_cons_ua, 8 },
+    { "data16.ua", stmt_cons_ua, 16 },
     { "real4.ua", float_cons, 'f' },
     { "real8.ua", float_cons, 'd' },
     { "real10.ua", float_cons, 'x' },
+    { "real16.ua", float_cons, 'X' },
   };
 
 /* Declare a register by creating a symbol for it and entering it in
@@ -4840,6 +5475,11 @@ operand_match (idesc, index, e)
        return OPERAND_MATCH;
       break;
 
+    case IA64_OPND_AR_CSD:
+      if (e->X_op == O_register && e->X_add_number == REG_AR + 25)
+       return OPERAND_MATCH;
+      break;
+
     case IA64_OPND_AR_PFS:
       if (e->X_op == O_register && e->X_add_number == REG_AR + 64)
        return OPERAND_MATCH;
@@ -4957,7 +5597,7 @@ operand_match (idesc, index, e)
 
     case IA64_OPND_R3_2:
       if (e->X_op == O_register && e->X_add_number >= REG_GR)
-       { 
+       {
          if (e->X_add_number < REG_GR + 4)
            return OPERAND_MATCH;
          else if (e->X_add_number < REG_GR + 128)
@@ -5331,6 +5971,15 @@ operand_match (idesc, index, e)
        }
       break;
 
+    case IA64_OPND_LDXMOV:
+      fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
+      fix->code = BFD_RELOC_IA64_LDXMOV;
+      fix->opnd = idesc->operands[index];
+      fix->expr = *e;
+      fix->is_pcrel = 0;
+      ++CURR_SLOT.num_fixups;
+      return OPERAND_MATCH;
+
     default:
       break;
     }
@@ -5383,6 +6032,8 @@ parse_operands (idesc)
 {
   int i = 0, highest_unmatched_operand, num_operands = 0, num_outputs = 0;
   int error_pos, out_of_range_pos, curr_out_of_range_pos, sep = 0;
+  int reg1, reg2;
+  char reg_class;
   enum ia64_opnd expected_operand = IA64_OPND_NIL;
   enum operand_match_result result;
   char mnemonic[129];
@@ -5392,7 +6043,8 @@ parse_operands (idesc)
   assert (strlen (idesc->name) <= 128);
 
   strcpy (mnemonic, idesc->name);
-  if (idesc->operands[2] == IA64_OPND_SOF)
+  if (idesc->operands[2] == IA64_OPND_SOF
+      || idesc->operands[1] == IA64_OPND_SOF)
     {
       /* To make the common idiom "alloc loc?=ar.pfs,0,1,0,0" work, we
         can't parse the first operand until we have parsed the
@@ -5410,11 +6062,22 @@ parse_operands (idesc)
       ++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;
 
@@ -5435,20 +6098,23 @@ parse_operands (idesc)
       return 0;
     }
 
-  if (idesc->operands[2] == IA64_OPND_SOF)
+  if (idesc->operands[2] == IA64_OPND_SOF
+      || idesc->operands[1] == IA64_OPND_SOF)
     {
       /* map alloc r1=ar.pfs,i,l,o,r to alloc r1=ar.pfs,(i+l+o),(i+l),r */
       know (strcmp (idesc->name, "alloc") == 0);
-      if (num_operands == 5 /* first_arg not included in this count! */
-         && CURR_SLOT.opnd[2].X_op == O_constant
-         && CURR_SLOT.opnd[3].X_op == O_constant
-         && CURR_SLOT.opnd[4].X_op == O_constant
-         && CURR_SLOT.opnd[5].X_op == O_constant)
-       {
-         sof = set_regstack (CURR_SLOT.opnd[2].X_add_number,
-                             CURR_SLOT.opnd[3].X_add_number,
-                             CURR_SLOT.opnd[4].X_add_number,
-                             CURR_SLOT.opnd[5].X_add_number);
+      i = (CURR_SLOT.opnd[1].X_op == O_register
+          && CURR_SLOT.opnd[1].X_add_number == REG_AR + AR_PFS) ? 2 : 1;
+      if (num_operands == i + 3 /* first_arg not included in this count! */
+         && CURR_SLOT.opnd[i].X_op == O_constant
+         && CURR_SLOT.opnd[i + 1].X_op == O_constant
+         && CURR_SLOT.opnd[i + 2].X_op == O_constant
+         && CURR_SLOT.opnd[i + 3].X_op == O_constant)
+       {
+         sof = set_regstack (CURR_SLOT.opnd[i].X_add_number,
+                             CURR_SLOT.opnd[i + 1].X_add_number,
+                             CURR_SLOT.opnd[i + 2].X_add_number,
+                             CURR_SLOT.opnd[i + 3].X_add_number);
 
          /* now we can parse the first arg:  */
          saved_input_pointer = input_line_pointer;
@@ -5458,21 +6124,29 @@ parse_operands (idesc)
            --num_outputs;      /* force error */
          input_line_pointer = saved_input_pointer;
 
-         CURR_SLOT.opnd[2].X_add_number = sof;
-         CURR_SLOT.opnd[3].X_add_number
-           = sof - CURR_SLOT.opnd[4].X_add_number;
-         CURR_SLOT.opnd[4] = CURR_SLOT.opnd[5];
+         CURR_SLOT.opnd[i].X_add_number = sof;
+         CURR_SLOT.opnd[i + 1].X_add_number
+           = sof - CURR_SLOT.opnd[i + 2].X_add_number;
+         CURR_SLOT.opnd[i + 2] = CURR_SLOT.opnd[i + 3];
        }
     }
 
-  highest_unmatched_operand = 0;
+  highest_unmatched_operand = -4;
   curr_out_of_range_pos = -1;
   error_pos = 0;
-  expected_operand = idesc->operands[0];
   for (; idesc; idesc = get_next_opcode (idesc))
     {
       if (num_outputs != idesc->num_outputs)
        continue;               /* mismatch in # of outputs */
+      if (highest_unmatched_operand < 0)
+       highest_unmatched_operand |= 1;
+      if (num_operands > NELEMS (idesc->operands)
+         || (num_operands < NELEMS (idesc->operands)
+          && idesc->operands[num_operands])
+         || (num_operands > 0 && !idesc->operands[num_operands - 1]))
+       continue;               /* mismatch in number of arguments */
+      if (highest_unmatched_operand < 0)
+       highest_unmatched_operand |= 2;
 
       CURR_SLOT.num_fixups = 0;
 
@@ -5525,10 +6199,6 @@ parse_operands (idesc)
          continue;
        }
 
-      if (num_operands < NELEMS (idesc->operands)
-         && idesc->operands[num_operands])
-       continue;               /* mismatch in number of arguments */
-
       break;
     }
   if (!idesc)
@@ -5537,103 +6207,136 @@ parse_operands (idesc)
        as_bad ("Operand %u of `%s' should be %s",
                error_pos + 1, mnemonic,
                elf64_ia64_operands[expected_operand].desc);
+      else if (highest_unmatched_operand < 0 && !(highest_unmatched_operand & 1))
+       as_bad ("Wrong number of output operands");
+      else if (highest_unmatched_operand < 0 && !(highest_unmatched_operand & 2))
+       as_bad ("Wrong number of input operands");
       else
        as_bad ("Operand mismatch");
       return 0;
     }
-  return idesc;
-}
-
-/* Keep track of state necessary to determine whether a NOP is necessary
-   to avoid an erratum in A and B step Itanium chips, and return 1 if we
-   detect a case where additional NOPs may be necessary.  */
-static int
-errata_nop_necessary_p (slot, insn_unit)
-     struct slot *slot;
-     enum ia64_unit insn_unit;
-{
-  int i;
-  struct group *this_group = md.last_groups + md.group_idx;
-  struct group *prev_group = md.last_groups + (md.group_idx + 2) % 3;
-  struct ia64_opcode *idesc = slot->idesc;
-
-  /* Test whether this could be the first insn in a problematic sequence.  */
-  if (insn_unit == IA64_UNIT_F)
-    {
-      for (i = 0; i < idesc->num_outputs; i++)
-       if (idesc->operands[i] == IA64_OPND_P1
-           || idesc->operands[i] == IA64_OPND_P2)
-         {
-           int regno = slot->opnd[i].X_add_number - REG_P;
-           /* Ignore invalid operands; they generate errors elsewhere.  */
-           if (regno >= 64)
-             return 0;
-           this_group->p_reg_set[regno] = 1;
-         }
-    }
 
-  /* Test whether this could be the second insn in a problematic sequence.  */
-  if (insn_unit == IA64_UNIT_M && slot->qp_regno > 0
-      && prev_group->p_reg_set[slot->qp_regno])
+  /* Check that the instruction doesn't use
+     - r0, f0, or f1 as output operands
+     - the same predicate twice as output operands
+     - r0 as address of a base update load or store
+     - the same GR as output and address of a base update load
+     - two even- or two odd-numbered FRs as output operands of a floating
+       point parallel load.
+     At most two (conflicting) output (or output-like) operands can exist,
+     (floating point parallel loads have three outputs, but the base register,
+     if updated, cannot conflict with the actual outputs).  */
+  reg2 = reg1 = -1;
+  for (i = 0; i < num_operands; ++i)
     {
-      for (i = 0; i < idesc->num_outputs; i++)
-       if (idesc->operands[i] == IA64_OPND_R1
-           || idesc->operands[i] == IA64_OPND_R2
-           || idesc->operands[i] == IA64_OPND_R3)
-         {
-           int regno = slot->opnd[i].X_add_number - REG_GR;
-           /* Ignore invalid operands; they generate errors elsewhere.  */
-           if (regno >= 128)
-             return 0;
-           if (strncmp (idesc->name, "add", 3) != 0
-               && strncmp (idesc->name, "sub", 3) != 0
-               && strncmp (idesc->name, "shladd", 6) != 0
-               && (idesc->flags & IA64_OPCODE_POSTINC) == 0)
-             this_group->g_reg_set_conditionally[regno] = 1;
-         }
-    }
+      int regno = 0;
 
-  /* Test whether this could be the third insn in a problematic sequence.  */
-  for (i = 0; i < NELEMS (idesc->operands) && idesc->operands[i]; i++)
-    {
-      if (/* For fc, ptc, ptr, tak, thash, tpa, ttag, probe, ptr, ptc.  */
-         idesc->operands[i] == IA64_OPND_R3
-         /* For mov indirect.  */
-         || idesc->operands[i] == IA64_OPND_RR_R3
-         || idesc->operands[i] == IA64_OPND_DBR_R3
-         || idesc->operands[i] == IA64_OPND_IBR_R3
-         || idesc->operands[i] == IA64_OPND_PKR_R3
-         || idesc->operands[i] == IA64_OPND_PMC_R3
-         || idesc->operands[i] == IA64_OPND_PMD_R3
-         || idesc->operands[i] == IA64_OPND_MSR_R3
-         || idesc->operands[i] == IA64_OPND_CPUID_R3
-         /* For itr.  */
-         || idesc->operands[i] == IA64_OPND_ITR_R3
-         || idesc->operands[i] == IA64_OPND_DTR_R3
-         /* Normal memory addresses (load, store, xchg, cmpxchg, etc.).  */
-         || idesc->operands[i] == IA64_OPND_MR3)
-       {
-         int regno = slot->opnd[i].X_add_number - REG_GR;
-         /* Ignore invalid operands; they generate errors elsewhere.  */
-         if (regno >= 128)
-           return 0;
-         if (idesc->operands[i] == IA64_OPND_R3)
-           {
-             if (strcmp (idesc->name, "fc") != 0
-                 && strcmp (idesc->name, "tak") != 0
-                 && strcmp (idesc->name, "thash") != 0
-                 && strcmp (idesc->name, "tpa") != 0
-                 && strcmp (idesc->name, "ttag") != 0
-                 && strncmp (idesc->name, "ptr", 3) != 0
-                 && strncmp (idesc->name, "ptc", 3) != 0
-                 && strncmp (idesc->name, "probe", 5) != 0)
-             return 0;
-           }
-         if (prev_group->g_reg_set_conditionally[regno])
-           return 1;
+      reg_class = 0;
+      switch (idesc->operands[i])
+       {
+       case IA64_OPND_R1:
+       case IA64_OPND_R2:
+       case IA64_OPND_R3:
+         if (i < num_outputs)
+           {
+             if (CURR_SLOT.opnd[i].X_add_number == REG_GR)
+               reg_class = 'r';
+             else if (reg1 < 0)
+               reg1 = CURR_SLOT.opnd[i].X_add_number;
+             else if (reg2 < 0)
+               reg2 = CURR_SLOT.opnd[i].X_add_number;
+           }
+         break;
+       case IA64_OPND_P1:
+       case IA64_OPND_P2:
+         if (i < num_outputs)
+           {
+             if (reg1 < 0)
+               reg1 = CURR_SLOT.opnd[i].X_add_number;
+             else if (reg2 < 0)
+               reg2 = CURR_SLOT.opnd[i].X_add_number;
+           }
+         break;
+       case IA64_OPND_F1:
+       case IA64_OPND_F2:
+       case IA64_OPND_F3:
+       case IA64_OPND_F4:
+         if (i < num_outputs)
+           {
+             if (CURR_SLOT.opnd[i].X_add_number >= REG_FR
+                 && CURR_SLOT.opnd[i].X_add_number <= REG_FR + 1)
+               {
+                 reg_class = 'f';
+                 regno = CURR_SLOT.opnd[i].X_add_number - REG_FR;
+               }
+             else if (reg1 < 0)
+               reg1 = CURR_SLOT.opnd[i].X_add_number;
+             else if (reg2 < 0)
+               reg2 = CURR_SLOT.opnd[i].X_add_number;
+           }
+         break;
+       case IA64_OPND_MR3:
+         if (idesc->flags & IA64_OPCODE_POSTINC)
+           {
+             if (CURR_SLOT.opnd[i].X_add_number == REG_GR)
+               reg_class = 'm';
+             else if (reg1 < 0)
+               reg1 = CURR_SLOT.opnd[i].X_add_number;
+             else if (reg2 < 0)
+               reg2 = CURR_SLOT.opnd[i].X_add_number;
+           }
+         break;
+       default:
+         break;
+       }
+      switch (reg_class)
+       {
+       case 0:
+         break;
+       default:
+         as_warn ("Invalid use of `%c%d' as output operand", reg_class, regno);
+         break;
+       case 'm':
+         as_warn ("Invalid use of `r%d' as base update address operand", regno);
+         break;
        }
     }
-  return 0;
+  if (reg1 == reg2)
+    {
+      if (reg1 >= REG_GR && reg1 <= REG_GR + 127)
+       {
+         reg1 -= REG_GR;
+         reg_class = 'r';
+       }
+      else if (reg1 >= REG_P && reg1 <= REG_P + 63)
+       {
+         reg1 -= REG_P;
+         reg_class = 'p';
+       }
+      else if (reg1 >= REG_FR && reg1 <= REG_FR + 127)
+       {
+         reg1 -= REG_FR;
+         reg_class = 'f';
+       }
+      else
+       reg_class = 0;
+      if (reg_class)
+       as_warn ("Invalid duplicate use of `%c%d'", reg_class, reg1);
+    }
+  else if (((reg1 >= REG_FR && reg1 <= REG_FR + 31
+            && reg2 >= REG_FR && reg2 <= REG_FR + 31)
+           || (reg1 >= REG_FR + 32 && reg1 <= REG_FR + 127
+            && reg2 >= REG_FR + 32 && reg2 <= REG_FR + 127))
+          && ! ((reg1 ^ reg2) & 1))
+    as_warn ("Invalid simultaneous use of `f%d' and `f%d'",
+            reg1 - REG_FR, reg2 - REG_FR);
+  else if ((reg1 >= REG_FR && reg1 <= REG_FR + 31
+           && reg2 >= REG_FR + 32 && reg2 <= REG_FR + 127)
+          || (reg1 >= REG_FR + 32 && reg1 <= REG_FR + 127
+           && reg2 >= REG_FR && reg2 <= REG_FR + 31))
+    as_warn ("Dangerous simultaneous use of `f%d' and `f%d'",
+            reg1 - REG_FR, reg2 - REG_FR);
+  return idesc;
 }
 
 static void
@@ -5643,7 +6346,8 @@ build_insn (slot, insnp)
 {
   const struct ia64_operand *odesc, *o2desc;
   struct ia64_opcode *idesc = slot->idesc;
-  bfd_signed_vma insn, val;
+  bfd_vma insn;
+  bfd_signed_vma val;
   const char *err;
   int i;
 
@@ -5763,22 +6467,22 @@ build_insn (slot, insnp)
 static void
 emit_one_bundle ()
 {
-  unsigned int manual_bundling_on = 0, manual_bundling_off = 0;
-  unsigned int manual_bundling = 0;
+  int manual_bundling_off = 0, manual_bundling = 0;
   enum ia64_unit required_unit, insn_unit = 0;
   enum ia64_insn_type type[3], insn_type;
   unsigned int template, orig_template;
   bfd_vma insn[3] = { -1, -1, -1 };
   struct ia64_opcode *idesc;
   int end_of_insn_group = 0, user_template = -1;
-  int n, i, j, first, curr;
-  unw_rec_list *ptr;
+  int n, i, j, first, curr, last_slot;
+  unw_rec_list *ptr, *last_ptr, *end_ptr;
   bfd_vma t0 = 0, t1 = 0;
   struct label_fix *lfix;
   struct insn_fix *ifix;
   char mnemonic[16];
   fixS *fix;
   char *f;
+  int addr_mod;
 
   first = (md.curr_slot + NUM_SLOTS - md.num_slots_in_use) % NUM_SLOTS;
   know (first >= 0 & first < NUM_SLOTS);
@@ -5810,34 +6514,81 @@ emit_one_bundle ()
 
   f = frag_more (16);
 
+  /* Check to see if this bundle is at an offset that is a multiple of 16-bytes
+     from the start of the frag.  */
+  addr_mod = frag_now_fix () & 15;
+  if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+    as_bad (_("instruction address is not a multiple of 16"));
+  frag_now->insn_addr = addr_mod;
+  frag_now->has_code = 1;
+
   /* now fill in slots with as many insns as possible:  */
   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)
     {
-      /* Set the slot number for prologue/body records now as those
-        refer to the current point, not the point after the
-        instruction has been issued:  */
-      /* Don't try to delete prologue/body records here, as that will cause
-        them to also be deleted from the master list of unwind records.  */
-      for (ptr = md.slot[curr].unwind_record; ptr; ptr = ptr->next)
-       if (ptr->r.type == prologue || ptr->r.type == prologue_gr
-           || ptr->r.type == body)
-         {
-           ptr->slot_number = (unsigned long) f + i;
-           ptr->slot_frag = frag_now;
-         }
+      /* If we have unwind records, we may need to update some now.  */
+      ptr = md.slot[curr].unwind_record;
+      if (ptr)
+       {
+         /* Find the last prologue/body record in the list for the current
+            insn, and set the slot number for all records up to that point.
+            This needs to be done now, because prologue/body records refer to
+            the current point, not the point after the instruction has been
+            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)
+           if (ptr->r.type == prologue || ptr->r.type == prologue_gr
+               || ptr->r.type == body)
+             last_ptr = ptr;
+         if (last_ptr)
+           {
+             /* Make last_ptr point one after the last prologue/body
+                record.  */
+             last_ptr = last_ptr->next;
+             for (ptr = md.slot[curr].unwind_record; ptr != last_ptr;
+                  ptr = ptr->next)
+               {
+                 ptr->slot_number = (unsigned long) f + i;
+                 ptr->slot_frag = frag_now;
+               }
+             /* Remove the initialized records, so that we won't accidentally
+                update them again if we insert a nop and continue.  */
+             md.slot[curr].unwind_record = last_ptr;
+           }
+       }
 
-      if (idesc->flags & IA64_OPCODE_SLOT2)
+      manual_bundling_off = md.slot[curr].manual_bundling_off;
+      if (md.slot[curr].manual_bundling_on)
        {
-         if (manual_bundling && i != 2)
-           as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
-                         "`%s' must be last in bundle", idesc->name);
+         if (curr == first)
+           manual_bundling = 1;
          else
-           i = 2;
+         break; /* Need to start a new bundle.  */
        }
-      if (idesc->flags & IA64_OPCODE_LAST)
+
+      /* 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)
        {
          int required_slot;
          unsigned int required_template;
@@ -5868,10 +6619,19 @@ emit_one_bundle ()
              required_slot = i;
              break;
            }
-         if (manual_bundling && i != required_slot)
-           as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
-                         "`%s' must be last in instruction group",
-                         idesc->name);
+         if (manual_bundling
+             && (i > required_slot
+                 || (required_slot == 2 && !manual_bundling_off)
+                 || (user_template >= 0
+                     /* Changing from MMI to M;MI is OK.  */
+                     && (template ^ required_template) > 1)))
+           {
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                           "`%s' must be last in instruction group",
+                           idesc->name);
+             if (i < 2 && required_slot == 2 && !manual_bundling_off)
+               manual_bundling = -1; /* Suppress meaningless post-loop errors.  */
+           }
          if (required_slot < i)
            /* Can't fit this instruction.  */
            break;
@@ -5890,24 +6650,16 @@ emit_one_bundle ()
        }
       if (curr != first && md.slot[curr].label_fixups)
        {
-         if (manual_bundling_on)
-           as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+         if (manual_bundling)
+           {
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
                          "Label must be first in a bundle");
+             manual_bundling = -1; /* Suppress meaningless post-loop errors.  */
+           }
          /* This insn must go into the first slot of a bundle.  */
          break;
        }
 
-      manual_bundling_on = md.slot[curr].manual_bundling_on;
-      manual_bundling_off = md.slot[curr].manual_bundling_off;
-
-      if (manual_bundling_on)
-       {
-         if (curr == first)
-           manual_bundling = 1;
-         else
-           break;                      /* need to start a new bundle */
-       }
-
       if (end_of_insn_group && md.num_slots_in_use >= 1)
        {
          /* We need an instruction group boundary in the middle of a
@@ -5935,12 +6687,17 @@ emit_one_bundle ()
                      reason we have to check for this is that otherwise we
                      may end up generating "MI;;I M.." which has the deadly
                      effect that the second M instruction is no longer the
-                     first in the bundle! --davidm 99/12/16  */
+                     first in the group! --davidm 99/12/16  */
                   && (idesc->flags & IA64_OPCODE_FIRST) == 0)
            {
              template = 1;
              end_of_insn_group = 0;
            }
+         else if (i == 1
+                  && user_template == 0
+                  && !(idesc->flags & IA64_OPCODE_FIRST))
+           /* Use the next slot.  */
+           continue;
          else if (curr != first)
            /* can't fit this insn */
            break;
@@ -5955,27 +6712,65 @@ emit_one_bundle ()
        }
       required_unit = ia64_templ_desc[template].exec_unit[i];
 
-      /* resolve dynamic opcodes such as "break" and "nop":  */
+      /* resolve dynamic opcodes such as "break", "hint", and "nop":  */
       if (idesc->type == IA64_TYPE_DYN)
        {
+         enum ia64_opnd opnd1, opnd2;
+
          if ((strcmp (idesc->name, "nop") == 0)
              || (strcmp (idesc->name, "break") == 0))
            insn_unit = required_unit;
-         else if (strcmp (idesc->name, "chk.s") == 0)
+         else if (strcmp (idesc->name, "hint") == 0)
+           {
+             insn_unit = required_unit;
+             if (required_unit == IA64_UNIT_B)
+               {
+                 switch (md.hint_b)
+                   {
+                   case hint_b_ok:
+                     break;
+                   case hint_b_warning:
+                     as_warn ("hint in B unit may be treated as nop");
+                     break;
+                   case hint_b_error:
+                     /* When manual bundling is off and there is no
+                        user template, we choose a different unit so
+                        that hint won't go into the current slot. We
+                        will fill the current bundle with nops and
+                        try to put hint into the next bundle.  */
+                     if (!manual_bundling && user_template < 0)
+                       insn_unit = IA64_UNIT_I;
+                     else
+                       as_bad ("hint in B unit can't be used");
+                     break;
+                   }
+               }
+           }
+         else if (strcmp (idesc->name, "chk.s") == 0
+             || strcmp (idesc->name, "mov") == 0)
            {
              insn_unit = IA64_UNIT_M;
-             if (required_unit == IA64_UNIT_I)
+             if (required_unit == IA64_UNIT_I
+                 || (required_unit == IA64_UNIT_F && template == 6))
                insn_unit = IA64_UNIT_I;
            }
          else
            as_fatal ("emit_one_bundle: unexpected dynamic op");
 
-         sprintf (mnemonic, "%s.%c", idesc->name, "?imbf??"[insn_unit]);
+         sprintf (mnemonic, "%s.%c", idesc->name, "?imbfxx"[insn_unit]);
+         opnd1 = idesc->operands[0];
+         opnd2 = idesc->operands[1];
          ia64_free_opcode (idesc);
-         md.slot[curr].idesc = idesc = ia64_find_opcode (mnemonic);
-#if 0
-         know (!idesc->next);  /* no resolved dynamic ops have collisions */
-#endif
+         idesc = ia64_find_opcode (mnemonic);
+         /* moves to/from ARs have collisions */
+         if (opnd1 == IA64_OPND_AR3 || opnd2 == IA64_OPND_AR3)
+           {
+             while (idesc != NULL
+                    && (idesc->operands[0] != opnd1
+                        || idesc->operands[1] != opnd2))
+               idesc = get_next_opcode (idesc);
+           }
+         md.slot[curr].idesc = idesc;
        }
       else
        {
@@ -5997,49 +6792,32 @@ emit_one_bundle ()
        }
 
       if (insn_unit != required_unit)
-       {
-         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 */
-       }
+       continue;               /* Try next slot.  */
 
+      if (debug_type == DEBUG_DWARF2 || md.slot[curr].loc_directive_seen)
        {
-         bfd_vma addr;
+         bfd_vma addr = frag_now->fr_address + frag_now_fix () - 16 + i;
 
-         addr = frag_now->fr_address + frag_now_fix () - 16 + i;
+         md.slot[curr].loc_directive_seen = 0;
          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);
 
-      /* Set slot counts for non prologue/body unwind records.  */
-      for (ptr = md.slot[curr].unwind_record; ptr; ptr = ptr->next)
-       if (ptr->r.type != prologue && ptr->r.type != prologue_gr
-           && ptr->r.type != body)
-         {
-           ptr->slot_number = (unsigned long) f + i;
-           ptr->slot_frag = frag_now;
-         }
-      md.slot[curr].unwind_record = NULL;
+      ptr = md.slot[curr].unwind_record;
+      if (ptr)
+       {
+         /* 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;
+             ptr->slot_frag = frag_now;
+           }
+         md.slot[curr].unwind_record = NULL;
+       }
 
       if (required_unit == IA64_UNIT_L)
        {
@@ -6048,6 +6826,7 @@ emit_one_bundle ()
          ++i;
        }
       --md.num_slots_in_use;
+      last_slot = i;
 
       /* now is a good time to fix up the labels for this insn:  */
       for (lfix = md.slot[curr].label_fixups; lfix; lfix = lfix->next)
@@ -6075,12 +6854,6 @@ emit_one_bundle ()
 
       end_of_insn_group = md.slot[curr].end_of_insn_group;
 
-      if (end_of_insn_group)
-       {
-         md.group_idx = (md.group_idx + 1) % 3;
-         memset (md.last_groups + md.group_idx, 0, sizeof md.last_groups[0]);
-       }
-
       /* clear slot:  */
       ia64_free_opcode (md.slot[curr].idesc);
       memset (md.slot + curr, 0, sizeof (md.slot[curr]));
@@ -6094,12 +6867,40 @@ emit_one_bundle ()
       curr = (curr + 1) % NUM_SLOTS;
       idesc = md.slot[curr].idesc;
     }
-  if (manual_bundling)
+  if (manual_bundling > 0)
     {
       if (md.num_slots_in_use > 0)
-       as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
-                     "`%s' does not fit into %s template",
-                     idesc->name, ia64_templ_desc[template].name);
+       {
+         if (last_slot >= 2)
+           as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                         "`%s' does not fit into bundle", idesc->name);
+         else if (last_slot < 0)
+           {
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                           "`%s' does not fit into %s template",
+                           idesc->name, ia64_templ_desc[template].name);
+             /* Drop first insn so we don't livelock.  */
+             --md.num_slots_in_use;
+             know (curr == first);
+             ia64_free_opcode (md.slot[curr].idesc);
+             memset (md.slot + curr, 0, sizeof (md.slot[curr]));
+             md.slot[curr].user_template = -1;
+           }
+         else
+           {
+             const char *where;
+
+             if (template == 2)
+               where = "X slot";
+             else if (last_slot == 0)
+               where = "slots 2 or 3";
+             else
+               where = "slot 3";
+             as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+                           "`%s' can't go in %s of %s template",
+                           idesc->name, where, ia64_templ_desc[template].name);
+           }
+       }
       else
        as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
                      "Missing '}' at end of file");
@@ -6112,8 +6913,11 @@ emit_one_bundle ()
   number_to_chars_littleendian (f + 0, t0, 8);
   number_to_chars_littleendian (f + 8, t1, 8);
 
-  unwind.next_slot_number = (unsigned long) f + 16;
-  unwind.next_slot_frag = frag_now;
+  if (unwind.list)
+    {
+      unwind.list->next_slot_number = (unsigned long) f + 16;
+      unwind.list->next_slot_frag = frag_now;
+    }
 }
 
 int
@@ -6139,10 +6943,44 @@ md_parse_option (c, arg)
       else if (strcmp (arg, "le") == 0)
        {
          md.flags &= ~EF_IA_64_BE;
+         default_big_endian = 0;
        }
       else if (strcmp (arg, "be") == 0)
        {
          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;
@@ -6199,6 +7037,10 @@ md_parse_option (c, arg)
        {
          md.default_explicit_mode = 0;
        }
+      else if (strcmp (arg, "none") == 0)
+       {
+         md.detect_dv = 0;
+       }
       else if (strcmp (arg, "debug") == 0)
        {
          md.debug_dv = 1;
@@ -6208,6 +7050,11 @@ md_parse_option (c, arg)
          md.default_explicit_mode = 1;
          md.debug_dv = 1;
        }
+      else if (strcmp (arg, "debugn") == 0)
+       {
+         md.debug_dv = 1;
+         md.detect_dv = 0;
+       }
       else
        {
          as_bad (_("Unrecognized option '-x%s'"), arg);
@@ -6240,14 +7087,37 @@ md_show_usage (stream)
 {
   fputs (_("\
 IA-64 options:\n\
+  --mconstant-gp         mark output file as using the constant-GP model\n\
+                         (sets ELF header flag EF_IA_64_CONS_GP)\n\
+  --mauto-pic            mark output file as using the constant-GP model\n\
+                         without function descriptors (sets ELF header flag\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);
 }
 
+void
+ia64_after_parse_args ()
+{
+  if (debug_type == DEBUG_STABS)
+    as_fatal (_("--gstabs is not supported for ia64"));
+}
+
 /* Return true if TYPE fits in TEMPL at SLOT.  */
 
 static int
@@ -6279,11 +7149,30 @@ match (int templ, int type, 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
@@ -6301,7 +7190,23 @@ md_begin ()
 
   bfd_set_section_alignment (stdoutput, text_section, 4);
 
-  target_big_endian = TARGET_BYTES_BIG_ENDIAN;
+  /* Make sure function pointers get initialized.  */
+  target_big_endian = -1;
+  dot_byteorder (default_big_endian);
+
+  alias_hash = hash_new ();
+  alias_name_hash = hash_new ();
+  secalias_hash = hash_new ();
+  secalias_name_hash = hash_new ();
+
+  pseudo_func[FUNC_DTP_MODULE].u.sym =
+    symbol_new (".<dtpmod>", undefined_section, FUNC_DTP_MODULE,
+               &zero_address_frag);
+
+  pseudo_func[FUNC_DTP_RELATIVE].u.sym =
+    symbol_new (".<dtprel>", undefined_section, FUNC_DTP_RELATIVE,
+               &zero_address_frag);
+
   pseudo_func[FUNC_FPTR_RELATIVE].u.sym =
     symbol_new (".<fptr>", undefined_section, FUNC_FPTR_RELATIVE,
                &zero_address_frag);
@@ -6314,6 +7219,10 @@ md_begin ()
     symbol_new (".<ltoff>", undefined_section, FUNC_LT_RELATIVE,
                &zero_address_frag);
 
+  pseudo_func[FUNC_LT_RELATIVE_X].u.sym =
+    symbol_new (".<ltoffx>", undefined_section, FUNC_LT_RELATIVE_X,
+               &zero_address_frag);
+
   pseudo_func[FUNC_PC_RELATIVE].u.sym =
     symbol_new (".<pcrel>", undefined_section, FUNC_PC_RELATIVE,
                &zero_address_frag);
@@ -6330,6 +7239,10 @@ md_begin ()
     symbol_new (".<segrel>", undefined_section, FUNC_SEG_RELATIVE,
                &zero_address_frag);
 
+  pseudo_func[FUNC_TP_RELATIVE].u.sym =
+    symbol_new (".<tprel>", undefined_section, FUNC_TP_RELATIVE,
+               &zero_address_frag);
+
   pseudo_func[FUNC_LTV_RELATIVE].u.sym =
     symbol_new (".<ltv>", undefined_section, FUNC_LTV_RELATIVE,
                &zero_address_frag);
@@ -6338,11 +7251,33 @@ md_begin ()
     symbol_new (".<ltoff.fptr>", undefined_section, FUNC_LT_FPTR_RELATIVE,
                &zero_address_frag);
 
+  pseudo_func[FUNC_LT_DTP_MODULE].u.sym =
+    symbol_new (".<ltoff.dtpmod>", undefined_section, FUNC_LT_DTP_MODULE,
+               &zero_address_frag);
+
+  pseudo_func[FUNC_LT_DTP_RELATIVE].u.sym =
+    symbol_new (".<ltoff.dptrel>", undefined_section, FUNC_LT_DTP_RELATIVE,
+               &zero_address_frag);
+
+  pseudo_func[FUNC_LT_TP_RELATIVE].u.sym =
+    symbol_new (".<ltoff.tprel>", undefined_section, FUNC_LT_TP_RELATIVE,
+               &zero_address_frag);
+
+  pseudo_func[FUNC_IPLT_RELOC].u.sym =
+    symbol_new (".<iplt>", undefined_section, FUNC_IPLT_RELOC,
+               &zero_address_frag);
+
+ if (md.tune != itanium1)
+   {
+     /* Convert MFI NOPs bundles into MMI NOPs bundles.  */
+     le_nop[0] = 0x8;
+     le_nop_stop[0] = 0x9;
+   }
+
   /* Compute the table of best templates.  We compute goodness as a
-     base 4 value, in which each match counts for 3, each F counts
-     for 2, each B counts for 1.  This should maximize the number of
-     F and B nops in the chosen bundles, which is good because these
-     pipelines are least likely to be overcommitted.  */
+     base 4 value, in which each match counts for 3.  Match-failures
+     result in NOPs and we use extra_goodness() to pick the execution
+     units that are best suited for issuing the NOP.  */
   for (i = 0; i < IA64_NUM_TYPES; ++i)
     for (j = 0; j < IA64_NUM_TYPES; ++j)
       for (k = 0; k < IA64_NUM_TYPES; ++k)
@@ -6529,19 +7464,21 @@ md_begin ()
   md.entry_labels = NULL;
 }
 
-/* Set the elf type to 64 bit ABI by default.  Cannot do this in md_begin
-   because that is called after md_parse_option which is where we do the
-   dynamic changing of md.flags based on -mlp64 or -milp32.  Also, set the
-   default endianness.  */
+/* Set the default options in md.  Cannot do this in md_begin because
+   that is called after md_parse_option which is where we set the
+   options in md based on command line options.  */
 
 void
 ia64_init (argc, argv)
      int argc ATTRIBUTE_UNUSED;
      char **argv ATTRIBUTE_UNUSED;
 {
-  md.flags = EF_IA_64_ABI64;
-  if (TARGET_BYTES_BIG_ENDIAN)
-    md.flags |= EF_IA_64_BE;
+  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.  */
@@ -6554,14 +7491,18 @@ ia64_target_format ()
       if (md.flags & EF_IA_64_BE)
        {
          if (md.flags & EF_IA_64_ABI64)
-#ifdef TE_AIX50
+#if defined(TE_AIX50)
            return "elf64-ia64-aix-big";
+#elif defined(TE_HPUX)
+           return "elf64-ia64-hpux-big";
 #else
            return "elf64-ia64-big";
 #endif
          else
-#ifdef TE_AIX50
+#if defined(TE_AIX50)
            return "elf32-ia64-aix-big";
+#elif defined(TE_HPUX)
+           return "elf32-ia64-hpux-big";
 #else
            return "elf32-ia64-big";
 #endif
@@ -6613,7 +7554,15 @@ ia64_start_line ()
   if (input_line_pointer[0] == ';' && input_line_pointer[-1] == ';')
     {
       if (md.detect_dv && !md.explicit_mode)
-       as_warn (_("Explicit stops are ignored in auto mode"));
+       {
+         static int warned;
+
+         if (!warned)
+           {
+             warned = 1;
+             as_warn (_("Explicit stops are ignored in auto mode"));
+           }
+       }
       else
        insn_group_break (1, 0, 0);
     }
@@ -6714,10 +7663,10 @@ ia64_unrecognized_line (ch)
            c = get_symbol_end ();
          }
        else if (LOCAL_LABELS_FB
-                && isdigit ((unsigned char) *input_line_pointer))
+                && ISDIGIT (*input_line_pointer))
          {
            temp = 0;
-           while (isdigit ((unsigned char) *input_line_pointer))
+           while (ISDIGIT (*input_line_pointer))
              temp = (temp * 10) + *input_line_pointer++ - '0';
            fb_label_instance_inc (temp);
            s = fb_label_name (temp, 0);
@@ -6800,6 +7749,23 @@ ia64_frob_label (sym)
     }
 }
 
+#ifdef TE_HPUX
+/* The HP-UX linker will give unresolved symbol errors for symbols
+   that are declared but unused.  This routine removes declared,
+   unused symbols from an object.  */
+int
+ia64_frob_symbol (sym)
+     struct symbol *sym;
+{
+  if ((S_GET_SEGMENT (sym) == &bfd_und_section && ! symbol_used_p (sym) &&
+       ELF_ST_VISIBILITY (S_GET_OTHER (sym)) == STV_DEFAULT)
+      || (S_GET_SEGMENT (sym) == &bfd_abs_section
+         && ! S_IS_EXTERNAL (sym)))
+    return 1;
+  return 0;
+}
+#endif
+
 void
 ia64_flush_pending_output ()
 {
@@ -6859,16 +7825,100 @@ ia64_optimize_expr (l, op, r)
 }
 
 int
-ia64_parse_name (name, e)
+ia64_parse_name (name, e, nextcharP)
      char *name;
      expressionS *e;
+     char *nextcharP;
 {
   struct const_desc *cdesc;
   struct dynreg *dr = 0;
-  unsigned int regnum;
+  unsigned int idx;
   struct symbol *sym;
   char *end;
 
+  if (*name == '@')
+    {
+      enum pseudo_type pseudo_type = PSEUDO_FUNC_NONE;
+
+      /* Find what relocation pseudo-function we're dealing with.  */
+      for (idx = 0; idx < NELEMS (pseudo_func); ++idx)
+       if (pseudo_func[idx].name
+           && pseudo_func[idx].name[0] == name[1]
+           && strcmp (pseudo_func[idx].name + 1, name + 2) == 0)
+         {
+           pseudo_type = pseudo_func[idx].type;
+           break;
+         }
+      switch (pseudo_type)
+       {
+       case PSEUDO_FUNC_RELOC:
+         end = input_line_pointer;
+         if (*nextcharP != '(')
+           {
+             as_bad ("Expected '('");
+             break;
+           }
+         /* Skip '('.  */
+         ++input_line_pointer;
+         expression (e);
+         if (*input_line_pointer != ')')
+           {
+             as_bad ("Missing ')'");
+             goto done;
+           }
+         /* Skip ')'.  */
+         ++input_line_pointer;
+         if (e->X_op != O_symbol)
+           {
+             if (e->X_op != O_pseudo_fixup)
+               {
+                 as_bad ("Not a symbolic expression");
+                 goto done;
+               }
+             if (idx != FUNC_LT_RELATIVE)
+               {
+                 as_bad ("Illegal combination of relocation functions");
+                 goto done;
+               }
+             switch (S_GET_VALUE (e->X_op_symbol))
+               {
+               case FUNC_FPTR_RELATIVE:
+                 idx = FUNC_LT_FPTR_RELATIVE; break;
+               case FUNC_DTP_MODULE:
+                 idx = FUNC_LT_DTP_MODULE; break;
+               case FUNC_DTP_RELATIVE:
+                 idx = FUNC_LT_DTP_RELATIVE; break;
+               case FUNC_TP_RELATIVE:
+                 idx = FUNC_LT_TP_RELATIVE; break;
+               default:
+                 as_bad ("Illegal combination of relocation functions");
+                 goto done;
+               }
+           }
+         /* Make sure gas doesn't get rid of local symbols that are used
+            in relocs.  */
+         e->X_op = O_pseudo_fixup;
+         e->X_op_symbol = pseudo_func[idx].u.sym;
+       done:
+         *nextcharP = *input_line_pointer;
+         break;
+
+       case PSEUDO_FUNC_CONST:
+         e->X_op = O_constant;
+         e->X_add_number = pseudo_func[idx].u.ival;
+         break;
+
+       case PSEUDO_FUNC_REG:
+         e->X_op = O_register;
+         e->X_add_number = pseudo_func[idx].u.ival;
+         break;
+
+       default:
+         return 0;
+       }
+      return 1;
+    }
+
   /* first see if NAME is a known register name:  */
   sym = hash_find (md.reg_hash, name);
   if (sym)
@@ -6887,29 +7937,30 @@ ia64_parse_name (name, e)
     }
 
   /* check for inN, locN, or outN:  */
+  idx = 0;
   switch (name[0])
     {
     case 'i':
-      if (name[1] == 'n' && isdigit (name[2]))
+      if (name[1] == 'n' && ISDIGIT (name[2]))
        {
          dr = &md.in;
-         name += 2;
+         idx = 2;
        }
       break;
 
     case 'l':
-      if (name[1] == 'o' && name[2] == 'c' && isdigit (name[3]))
+      if (name[1] == 'o' && name[2] == 'c' && ISDIGIT (name[3]))
        {
          dr = &md.loc;
-         name += 3;
+         idx = 3;
        }
       break;
 
     case 'o':
-      if (name[1] == 'u' && name[2] == 't' && isdigit (name[3]))
+      if (name[1] == 'u' && name[2] == 't' && ISDIGIT (name[3]))
        {
          dr = &md.out;
-         name += 3;
+         idx = 3;
        }
       break;
 
@@ -6917,13 +7968,16 @@ ia64_parse_name (name, e)
       break;
     }
 
-  if (dr)
+  /* Ignore register numbers with leading zeroes, except zero itself.  */
+  if (dr && (name[idx] != '0' || name[idx + 1] == '\0'))
     {
+      unsigned long regnum;
+
       /* The name is inN, locN, or outN; parse the register number.  */
-      regnum = strtoul (name, &end, 10);
-      if (end > name && *end == '\0')
+      regnum = strtoul (name + idx, &end, 10);
+      if (end > name + idx && *end == '\0' && regnum < 96)
        {
-         if ((unsigned) regnum >= dr->num_regs)
+         if (regnum >= dr->num_regs)
            {
              if (!dr->num_regs)
                as_bad ("No current frame");
@@ -6938,6 +7992,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.
@@ -6957,9 +8014,18 @@ char *
 ia64_canonicalize_symbol_name (name)
      char *name;
 {
-  size_t len = strlen (name);
-  if (len > 1 && name[len - 1] == '#')
-    name[len - 1] = '\0';
+  size_t len = strlen (name), full = len;
+
+  while (len > 0 && name[len - 1] == '#')
+    --len;
+  if (len <= 0)
+    {
+      if (full > 0)
+       as_bad ("Standalone `#' is illegal");
+    }
+  else if (len < full - 1)
+    as_warn ("Redundant `#' suffix operators");
+  name[len] = '\0';
   return name;
 }
 
@@ -7106,7 +8172,7 @@ specify_resource (dep, idesc, type, specs, note, path)
   tmpl.link_to_qp_branch = 1;
   tmpl.mem_offset.hint = 0;
   tmpl.specific = 1;
-  tmpl.index = 0;
+  tmpl.index = -1;
   tmpl.cmp_type = CMP_NONE;
 
 #define UNHANDLED \
@@ -7261,7 +8327,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
            }
          else
            {
-             for (i = idesc->num_outputs;i < NELEMS (idesc->operands); i++)
+             for (i = idesc->num_outputs; i < NELEMS (idesc->operands); i++)
                if (idesc->operands[i] == IA64_OPND_B1
                    || idesc->operands[i] == IA64_OPND_B2)
                  {
@@ -7783,7 +8849,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
              && idesc->operands[0] == IA64_OPND_PR)
            {
              mask = CURR_SLOT.opnd[2].X_add_number;
-             if (mask & ((valueT) 1<<16))
+             if (mask & ((valueT) 1 << 16))
                for (i = 16; i < 63; i++)
                  {
                    specs[count] = tmpl;
@@ -8274,13 +9340,13 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
          specs[count++] = tmpl;
        }
       else if (note == 7)
-        {
-          valueT mask = 0;
-          if (idesc->operands[2] == IA64_OPND_IMM17)
-            mask = CURR_SLOT.opnd[2].X_add_number;
-          if (mask & ((valueT) 1 << 63))
+       {
+         valueT mask = 0;
+         if (idesc->operands[2] == IA64_OPND_IMM17)
+           mask = CURR_SLOT.opnd[2].X_add_number;
+         if (mask & ((valueT) 1 << 63))
            specs[count++] = tmpl;
-        }
+       }
       else if (note == 11)
        {
          if ((idesc->operands[0] == IA64_OPND_P1
@@ -8302,8 +9368,8 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
        {
          if (rsrc_write)
            {
-              int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
-              int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+             int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
+             int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
              int or_andcm = strstr (idesc->name, "or.andcm") != NULL;
              int and_orcm = strstr (idesc->name, "and.orcm") != NULL;
 
@@ -8311,7 +9377,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
                  && (idesc->operands[0] == IA64_OPND_P1
                      || idesc->operands[0] == IA64_OPND_P2))
                {
-                  specs[count] = tmpl;
+                 specs[count] = tmpl;
                  specs[count++].cmp_type =
                    (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE));
                }
@@ -8319,7 +9385,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
                  && (idesc->operands[1] == IA64_OPND_P1
                      || idesc->operands[1] == IA64_OPND_P2))
                {
-                  specs[count] = tmpl;
+                 specs[count] = tmpl;
                  specs[count++].cmp_type =
                    (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE));
                }
@@ -8349,8 +9415,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
              if (idesc->operands[0] == IA64_OPND_AR3
                  && CURR_SLOT.opnd[0].X_add_number - REG_AR == AR_BSPSTORE)
                {
-                 specs[count] = tmpl;
-                 specs[count++].index = 0; /* IA64_RSE_BSPLOAD/RNATBITINDEX */
+                 specs[count++] = tmpl;
                }
            }
          else
@@ -8411,6 +9476,77 @@ clear_qp_branch_flag (mask)
     }
 }
 
+/* MASK contains 2 and only 2 PRs which are mutually exclusive.  Remove
+   any mutexes which contain one of the PRs and create new ones when
+   needed.  */
+
+static int
+update_qp_mutex (valueT mask)
+{
+  int i;
+  int add = 0;
+
+  i = 0;
+  while (i < qp_mutexeslen)
+    {
+      if ((qp_mutexes[i].prmask & mask) != 0)
+       {
+         /* If it destroys and creates the same mutex, do nothing.  */
+         if (qp_mutexes[i].prmask == mask
+             && qp_mutexes[i].path == md.path)
+           {
+             i++;
+             add = -1;
+           }
+         else
+           {
+             int keep = 0;
+
+             if (md.debug_dv)
+               {
+                 fprintf (stderr, "  Clearing mutex relation");
+                 print_prmask (qp_mutexes[i].prmask);
+                 fprintf (stderr, "\n");
+               }
+             
+             /* Deal with the old mutex with more than 3+ PRs only if
+                the new mutex on the same execution path with it.
+
+                FIXME: The 3+ mutex support is incomplete.
+                dot_pred_rel () may be a better place to fix it.  */
+             if (qp_mutexes[i].path == md.path)
+               {
+                 /* If it is a proper subset of the mutex, create a
+                    new mutex.  */
+                 if (add == 0
+                     && (qp_mutexes[i].prmask & mask) == mask)
+                   add = 1;
+                 
+                 qp_mutexes[i].prmask &= ~mask;
+                 if (qp_mutexes[i].prmask & (qp_mutexes[i].prmask - 1))
+                   {
+                     /* Modify the mutex if there are more than one
+                        PR left.  */
+                     keep = 1;
+                     i++;
+                   }
+               }
+             
+             if (keep == 0)
+               /* Remove the mutex.  */
+               qp_mutexes[i] = qp_mutexes[--qp_mutexeslen];
+           }
+       }
+      else
+       ++i;
+    }
+
+  if (add == 1)
+    add_qp_mutex (mask);
+
+  return add;
+}
+
 /* Remove any mutexes which contain any of the PRs indicated in the mask.
 
    Any changes to a PR clears the mutex relations which include that PR.  */
@@ -8556,6 +9692,19 @@ add_qp_mutex (mask)
   qp_mutexes[qp_mutexeslen++].prmask = mask;
 }
 
+static int
+has_suffix_p (name, suffix)
+     const char *name;
+     const char *suffix;
+{
+  size_t namelen = strlen (name);
+  size_t sufflen = strlen (suffix);
+
+  if (namelen <= sufflen)
+    return 0;
+  return strcmp (name + namelen - sufflen, suffix) == 0;
+}
+
 static void
 clear_register_values ()
 {
@@ -8612,7 +9761,7 @@ note_register_values (idesc)
       else if (idesc->operands[i] == IA64_OPND_PR_ROT)
        {
          if (idesc->operands[1] & ((valueT) 1 << 43))
-           qp_changemask = ~(valueT) 0xFFFFFFFFFFF | idesc->operands[1];
+           qp_changemask = -((valueT) 1 << 44) | idesc->operands[1];
          else
            qp_changemask = idesc->operands[1];
          qp_changemask &= ~(valueT) 0xFFFF;
@@ -8662,64 +9811,65 @@ note_register_values (idesc)
     {
       int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
       int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
-      valueT p1mask = (valueT) 1 << p1;
-      valueT p2mask = (valueT) 1 << p2;
+      valueT p1mask = (p1 != 0) ? (valueT) 1 << p1 : 0;
+      valueT p2mask = (p2 != 0) ? (valueT) 1 << p2 : 0;
 
-      /* If one of the PRs is PR0, we can't really do anything.  */
-      if (p1 == 0 || p2 == 0)
+      /* If both PRs are PR0, we can't really do anything.  */
+      if (p1 == 0 && p2 == 0)
        {
          if (md.debug_dv)
            fprintf (stderr, "  Ignoring PRs due to inclusion of p0\n");
        }
       /* In general, clear mutexes and implies which include P1 or P2,
         with the following exceptions.  */
-      else if (strstr (idesc->name, ".or.andcm") != NULL)
+      else if (has_suffix_p (idesc->name, ".or.andcm")
+              || has_suffix_p (idesc->name, ".and.orcm"))
        {
-         add_qp_mutex (p1mask | p2mask);
          clear_qp_implies (p2mask, p1mask);
        }
-      else if (strstr (idesc->name, ".and.orcm") != NULL)
-       {
-         add_qp_mutex (p1mask | p2mask);
-         clear_qp_implies (p1mask, p2mask);
-       }
-      else if (strstr (idesc->name, ".and") != NULL)
+      else if (has_suffix_p (idesc->name, ".andcm")
+              || has_suffix_p (idesc->name, ".and"))
        {
          clear_qp_implies (0, p1mask | p2mask);
        }
-      else if (strstr (idesc->name, ".or") != NULL)
+      else if (has_suffix_p (idesc->name, ".orcm")
+              || has_suffix_p (idesc->name, ".or"))
        {
          clear_qp_mutex (p1mask | p2mask);
          clear_qp_implies (p1mask | p2mask, 0);
        }
       else
        {
+         int added = 0;
+
          clear_qp_implies (p1mask | p2mask, p1mask | p2mask);
-         if (strstr (idesc->name, ".unc") != NULL)
+
+         /* If one of the PRs is PR0, we call clear_qp_mutex.  */
+         if (p1 == 0 || p2 == 0)
+           clear_qp_mutex (p1mask | p2mask);
+         else
+           added = update_qp_mutex (p1mask | p2mask);
+
+         if (CURR_SLOT.qp_regno == 0
+             || has_suffix_p (idesc->name, ".unc"))
            {
-             add_qp_mutex (p1mask | p2mask);
+             if (added == 0 && p1 && p2)
+               add_qp_mutex (p1mask | p2mask);
              if (CURR_SLOT.qp_regno != 0)
                {
-                 add_qp_imply (CURR_SLOT.opnd[0].X_add_number - REG_P,
-                               CURR_SLOT.qp_regno);
-                 add_qp_imply (CURR_SLOT.opnd[1].X_add_number - REG_P,
-                               CURR_SLOT.qp_regno);
+                 if (p1)
+                   add_qp_imply (p1, CURR_SLOT.qp_regno);
+                 if (p2)
+                   add_qp_imply (p2, CURR_SLOT.qp_regno);
                }
            }
-         else if (CURR_SLOT.qp_regno == 0)
-           {
-             add_qp_mutex (p1mask | p2mask);
-           }
-         else
-           {
-             clear_qp_mutex (p1mask | p2mask);
-           }
        }
     }
   /* Look for mov imm insns into GRs.  */
   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))
     {
@@ -8737,6 +9887,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);
@@ -8842,10 +10016,6 @@ resources_match (rs, idesc, note, qp_regno, path)
       else if (specs[count].index == rs->index)
        return 1;
     }
-#if 0
-  if (md.debug_dv)
-    fprintf (stderr, "  No %s conflicts\n", rs->dependency->name);
-#endif
 
   return 0;
 }
@@ -8961,7 +10131,7 @@ print_dependency (action, depind)
       fprintf (stderr, "  %s %s '%s'",
               action, dv_mode[(regdeps[depind].dependency)->mode],
               (regdeps[depind].dependency)->name);
-      if (regdeps[depind].specific && regdeps[depind].index != 0)
+      if (regdeps[depind].specific && regdeps[depind].index >= 0)
        fprintf (stderr, " (%d)", regdeps[depind].index);
       if (regdeps[depind].mem_offset.hint)
        {
@@ -9025,17 +10195,16 @@ remove_marked_resource (rs)
        insn_group_break (1, 0, 0);
       if (rs->insn_srlz < STATE_SRLZ)
        {
-         int oldqp = CURR_SLOT.qp_regno;
-         struct ia64_opcode *oldidesc = CURR_SLOT.idesc;
+         struct slot oldslot = CURR_SLOT;
          /* Manually jam a srlz.i insn into the stream */
-         CURR_SLOT.qp_regno = 0;
+         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;
          if (++md.num_slots_in_use >= NUM_SLOTS)
            emit_one_bundle ();
-         CURR_SLOT.qp_regno = oldqp;
-         CURR_SLOT.idesc = oldidesc;
+         CURR_SLOT = oldslot;
        }
       insn_group_break (1, 0, 0);
       break;
@@ -9048,17 +10217,16 @@ remove_marked_resource (rs)
       if (rs->data_srlz < STATE_STOP)
        insn_group_break (1, 0, 0);
       {
-       int oldqp = CURR_SLOT.qp_regno;
-       struct ia64_opcode *oldidesc = CURR_SLOT.idesc;
+       struct slot oldslot = CURR_SLOT;
        /* Manually jam a srlz.d insn into the stream */
-       CURR_SLOT.qp_regno = 0;
+       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;
        if (++md.num_slots_in_use >= NUM_SLOTS)
          emit_one_bundle ();
-       CURR_SLOT.qp_regno = oldqp;
-       CURR_SLOT.idesc = oldidesc;
+       CURR_SLOT = oldslot;
       }
       break;
     case IA64_DVS_IMPLIED:
@@ -9163,7 +10331,7 @@ check_dependencies (idesc)
              if (path != 0)
                sprintf (pathmsg, " when entry is at label '%s'",
                         md.entry_labels[path - 1]);
-             if (rs->specific && rs->index != 0)
+             if (matchtype == 1 && rs->index >= 0)
                sprintf (indexmsg, ", specific resource number is %d",
                         rs->index);
              sprintf (msg, "Use of '%s' %s %s dependency '%s' (%s)%s%s",
@@ -9250,12 +10418,6 @@ mark_resources (idesc)
 
       count = specify_resource (dep, idesc, DV_REG, specs, note, md.path);
 
-#if 0
-      if (md.debug_dv && !count)
-       fprintf (stderr, "  No %s %s usage found (path %d)\n",
-                dv_mode[dep->mode], dep->name, md.path);
-#endif
-
       while (count-- > 0)
        {
          mark_resource (idesc, dep, &specs[count],
@@ -9472,9 +10634,6 @@ md_assemble (str)
            mnemonic = "adds";
          ia64_free_opcode (idesc);
          idesc = ia64_find_opcode (mnemonic);
-#if 0
-         know (!idesc->next);
-#endif
        }
       else if (strcmp (idesc->name, "mov") == 0)
        {
@@ -9489,17 +10648,67 @@ md_assemble (str)
            rop = 1;
          else
            abort ();
-         if (CURR_SLOT.opnd[rop].X_op == O_register
-             && ar_is_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
-           mnemonic = "mov.i";
+         if (CURR_SLOT.opnd[rop].X_op == O_register)
+           {
+             if (ar_is_only_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
+               mnemonic = "mov.i";
+             else if (ar_is_only_in_memory_unit (CURR_SLOT.opnd[rop].X_add_number))
+               mnemonic = "mov.m";
+             else
+               rop = -1;
+           }
          else
-           mnemonic = "mov.m";
-         ia64_free_opcode (idesc);
-         idesc = ia64_find_opcode (mnemonic);
-         while (idesc != NULL
-                && (idesc->operands[0] != opnd1
-                    || idesc->operands[1] != opnd2))
-           idesc = get_next_opcode (idesc);
+           abort ();
+         if (rop >= 0)
+           {
+             ia64_free_opcode (idesc);
+             idesc = ia64_find_opcode (mnemonic);
+             while (idesc != NULL
+                    && (idesc->operands[0] != opnd1
+                        || idesc->operands[1] != opnd2))
+               idesc = get_next_opcode (idesc);
+           }
+       }
+    }
+  else if (strcmp (idesc->name, "mov.i") == 0
+          || strcmp (idesc->name, "mov.m") == 0)
+    {
+      enum ia64_opnd opnd1, opnd2;
+      int rop;
+      
+      opnd1 = idesc->operands[0];
+      opnd2 = idesc->operands[1];
+      if (opnd1 == IA64_OPND_AR3)
+       rop = 0;
+      else if (opnd2 == IA64_OPND_AR3)
+       rop = 1;
+      else
+       abort ();
+      if (CURR_SLOT.opnd[rop].X_op == O_register)
+       {
+         char unit = 'a';
+         if (ar_is_only_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
+           unit = 'i';
+         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",
+                   (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;
        }
     }
 
@@ -9513,7 +10722,27 @@ md_assemble (str)
   flags = idesc->flags;
 
   if ((flags & IA64_OPCODE_FIRST) != 0)
-    insn_group_break (1, 0, 0);
+    {
+      /* The alignment frag has to end with a stop bit only if the
+        next instruction after the alignment directive has to be
+        the first instruction in an instruction group.  */
+      if (align_frag)
+       {
+         while (align_frag->fr_type != rs_align_code)
+           {
+             align_frag = align_frag->fr_next;
+             if (!align_frag)
+               break;
+           }
+         /* align_frag can be NULL if there are directives in
+            between.  */
+         if (align_frag && align_frag->fr_next == frag_now)
+           align_frag->tc_frag_data = 1;
+       }
+
+      insn_group_break (1, 0, 0);
+    }
+  align_frag = NULL;
 
   if ((flags & IA64_OPCODE_NO_PRED) != 0 && qp_regno != 0)
     {
@@ -9533,6 +10762,8 @@ md_assemble (str)
       CURR_SLOT.unwind_record = unwind.current_entry;
       unwind.current_entry = NULL;
     }
+  if (unwind.proc_start && S_IS_DEFINED (unwind.proc_start))
+    unwind.insn = 1;
 
   /* Check for dependency violations.  */
   if (md.detect_dv)
@@ -9569,93 +10800,14 @@ void
 md_operand (e)
      expressionS *e;
 {
-  enum pseudo_type pseudo_type;
-  const char *name;
-  size_t len;
-  int ch, i;
-
   switch (*input_line_pointer)
     {
-    case '@':
-      /* Find what relocation pseudo-function we're dealing with.  */
-      pseudo_type = 0;
-      ch = *++input_line_pointer;
-      for (i = 0; i < NELEMS (pseudo_func); ++i)
-       if (pseudo_func[i].name && pseudo_func[i].name[0] == ch)
-         {
-           len = strlen (pseudo_func[i].name);
-           if (strncmp (pseudo_func[i].name + 1,
-                        input_line_pointer + 1, len - 1) == 0
-               && !is_part_of_name (input_line_pointer[len]))
-             {
-               input_line_pointer += len;
-               pseudo_type = pseudo_func[i].type;
-               break;
-             }
-         }
-      switch (pseudo_type)
-       {
-       case PSEUDO_FUNC_RELOC:
-         SKIP_WHITESPACE ();
-         if (*input_line_pointer != '(')
-           {
-             as_bad ("Expected '('");
-             goto err;
-           }
-         /* Skip '('.  */
-         ++input_line_pointer;
-         expression (e);
-         if (*input_line_pointer++ != ')')
-           {
-             as_bad ("Missing ')'");
-             goto err;
-           }
-         if (e->X_op != O_symbol)
-           {
-             if (e->X_op != O_pseudo_fixup)
-               {
-                 as_bad ("Not a symbolic expression");
-                 goto err;
-               }
-             if (S_GET_VALUE (e->X_op_symbol) == FUNC_FPTR_RELATIVE
-                 && i == FUNC_LT_RELATIVE)
-               i = FUNC_LT_FPTR_RELATIVE;
-             else
-               {
-                 as_bad ("Illegal combination of relocation functions");
-                 goto err;
-               }
-           }
-         /* Make sure gas doesn't get rid of local symbols that are used
-            in relocs.  */
-         e->X_op = O_pseudo_fixup;
-         e->X_op_symbol = pseudo_func[i].u.sym;
-         break;
-
-       case PSEUDO_FUNC_CONST:
-         e->X_op = O_constant;
-         e->X_add_number = pseudo_func[i].u.ival;
-         break;
-
-       case PSEUDO_FUNC_REG:
-         e->X_op = O_register;
-         e->X_add_number = pseudo_func[i].u.ival;
-         break;
-
-       default:
-         name = input_line_pointer - 1;
-         get_symbol_end ();
-         as_bad ("Unknown pseudo function `%s'", name);
-         goto err;
-       }
-      break;
-
     case '[':
       ++input_line_pointer;
       expression (e);
       if (*input_line_pointer != ']')
        {
-         as_bad ("Closing bracket misssing");
+         as_bad ("Closing bracket missing");
          goto err;
        }
       else
@@ -9726,12 +10878,16 @@ ia64_force_relocation (fix)
     case BFD_RELOC_IA64_PLTOFF64I:
     case BFD_RELOC_IA64_PLTOFF64MSB:
     case BFD_RELOC_IA64_PLTOFF64LSB:
+
+    case BFD_RELOC_IA64_LTOFF22X:
+    case BFD_RELOC_IA64_LDXMOV:
       return 1;
 
     default:
-      return 0;
+      break;
     }
-  return 0;
+
+  return generic_force_reloc (fix);
 }
 
 /* Decide from what point a pc-relative relocation is relative to,
@@ -9749,6 +10905,20 @@ ia64_pcrel_from_section (fix, sec)
   return off;
 }
 
+
+/* Used to emit section-relative relocs for the dwarf2 debug data.  */
+void
+ia64_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
+{
+  expressionS expr;
+
+  expr.X_op = O_pseudo_fixup;
+  expr.X_op_symbol = pseudo_func[FUNC_SEC_RELATIVE].u.sym;
+  expr.X_add_number = 0;
+  expr.X_add_symbol = symbol;
+  emit_expr (&expr, size);
+}
+
 /* This is called whenever some data item (not an instruction) needs a
    fixup.  We pick the right reloc code depending on the byteorder
    currently in effect.  */
@@ -9777,27 +10947,59 @@ ia64_cons_fix_new (f, where, nbytes, exp)
       break;
 
     case 8:
-      if (target_big_endian)
-       code = BFD_RELOC_IA64_DIR64MSB;
+      /* In 32-bit mode, data8 could mean function descriptors too.  */
+      if (exp->X_op == O_pseudo_fixup
+         && exp->X_op_symbol
+         && S_GET_VALUE (exp->X_op_symbol) == FUNC_IPLT_RELOC
+         && !(md.flags & EF_IA_64_ABI64))
+       {
+         if (target_big_endian)
+           code = BFD_RELOC_IA64_IPLTMSB;
+         else
+           code = BFD_RELOC_IA64_IPLTLSB;
+         exp->X_op = O_symbol;
+         break;
+       }
       else
-       code = BFD_RELOC_IA64_DIR64LSB;
-      break;
+       {
+         if (target_big_endian)
+           code = BFD_RELOC_IA64_DIR64MSB;
+         else
+           code = BFD_RELOC_IA64_DIR64LSB;
+         break;
+       }
+
+    case 16:
+      if (exp->X_op == O_pseudo_fixup
+         && exp->X_op_symbol
+         && S_GET_VALUE (exp->X_op_symbol) == FUNC_IPLT_RELOC)
+       {
+         if (target_big_endian)
+           code = BFD_RELOC_IA64_IPLTMSB;
+         else
+           code = BFD_RELOC_IA64_IPLTLSB;
+         exp->X_op = O_symbol;
+         break;
+       }
+      /* FALLTHRU */
 
     default:
       as_bad ("Unsupported fixup size %d", nbytes);
       ignore_rest_of_line ();
       return;
     }
+
   if (exp->X_op == O_pseudo_fixup)
     {
-      /* ??? */
       exp->X_op = O_symbol;
       code = ia64_gen_real_reloc_type (exp->X_op_symbol, code);
+      /* ??? If code unchanged, unsupported.  */
     }
+
   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_fix().  */
+     relocs available).  See md_apply_fix3().  */
   fix->tc_fix_data.bigendian = target_big_endian;
 }
 
@@ -9811,6 +11013,7 @@ ia64_gen_real_reloc_type (sym, r_type)
      bfd_reloc_code_real_type r_type;
 {
   bfd_reloc_code_real_type new = 0;
+  const char *type = NULL, *suffix = "";
 
   if (sym == NULL)
     {
@@ -9827,7 +11030,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_FPTR32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_FPTR64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_FPTR64LSB; break;
-       default:                        break;
+       default:                        type = "FPTR"; break;
        }
       break;
 
@@ -9840,7 +11043,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_GPREL32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_GPREL64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_GPREL64LSB; break;
-       default:                        break;
+       default:                        type = "GPREL"; break;
        }
       break;
 
@@ -9849,7 +11052,15 @@ ia64_gen_real_reloc_type (sym, r_type)
        {
        case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_LTOFF22; break;
        case BFD_RELOC_IA64_IMM64:      new = BFD_RELOC_IA64_LTOFF64I; break;
-       default:                        break;
+       default:                        type = "LTOFF"; break;
+       }
+      break;
+
+    case FUNC_LT_RELATIVE_X:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_LTOFF22X; break;
+       default:                        type = "LTOFF"; suffix = "X"; break;
        }
       break;
 
@@ -9862,7 +11073,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_PCREL32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_PCREL64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_PCREL64LSB; break;
-       default:                        break;
+       default:                        type = "PCREL"; break;
        }
       break;
 
@@ -9873,7 +11084,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_IMM64:      new = BFD_RELOC_IA64_PLTOFF64I; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_PLTOFF64MSB;break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_PLTOFF64LSB;break;
-       default:                        break;
+       default:                        type = "PLTOFF"; break;
        }
       break;
 
@@ -9884,7 +11095,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_SECREL32LSB;break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_SECREL64MSB;break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_SECREL64LSB;break;
-       default:                        break;
+       default:                        type = "SECREL"; break;
        }
       break;
 
@@ -9895,7 +11106,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_SEGREL32LSB;break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_SEGREL64MSB;break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_SEGREL64LSB;break;
-       default:                        break;
+       default:                        type = "SEGREL"; break;
        }
       break;
 
@@ -9906,7 +11117,7 @@ ia64_gen_real_reloc_type (sym, r_type)
        case BFD_RELOC_IA64_DIR32LSB:   new = BFD_RELOC_IA64_LTV32LSB; break;
        case BFD_RELOC_IA64_DIR64MSB:   new = BFD_RELOC_IA64_LTV64MSB; break;
        case BFD_RELOC_IA64_DIR64LSB:   new = BFD_RELOC_IA64_LTV64LSB; break;
-       default:                        break;
+       default:                        type = "LTV"; break;
        }
       break;
 
@@ -9917,18 +11128,133 @@ ia64_gen_real_reloc_type (sym, r_type)
          new = BFD_RELOC_IA64_LTOFF_FPTR22; break;
        case BFD_RELOC_IA64_IMM64:
          new = BFD_RELOC_IA64_LTOFF_FPTR64I; break;
+       case BFD_RELOC_IA64_DIR32MSB:
+         new = BFD_RELOC_IA64_LTOFF_FPTR32MSB; break;
+       case BFD_RELOC_IA64_DIR32LSB:
+         new = BFD_RELOC_IA64_LTOFF_FPTR32LSB; break;
+       case BFD_RELOC_IA64_DIR64MSB:
+         new = BFD_RELOC_IA64_LTOFF_FPTR64MSB; break;
+       case BFD_RELOC_IA64_DIR64LSB:
+         new = BFD_RELOC_IA64_LTOFF_FPTR64LSB; break;
        default:
-         break;
+         type = "LTOFF_FPTR"; break;
+       }
+      break;
+
+    case FUNC_TP_RELATIVE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM14:      new = BFD_RELOC_IA64_TPREL14; break;
+       case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_TPREL22; break;
+       case BFD_RELOC_IA64_IMM64:      new = BFD_RELOC_IA64_TPREL64I; break;
+       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;
+
+    case FUNC_LT_TP_RELATIVE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_LTOFF_TPREL22; break;
+       default:
+         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;
+
+    case FUNC_LT_DTP_MODULE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_LTOFF_DTPMOD22; break;
+       default:
+         type = "LTOFF_DTPMOD"; break;
+       }
+      break;
+
+    case FUNC_DTP_RELATIVE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_DIR32MSB:
+         new = BFD_RELOC_IA64_DTPREL32MSB; break;
+       case BFD_RELOC_IA64_DIR32LSB:
+         new = BFD_RELOC_IA64_DTPREL32LSB; break;
+       case BFD_RELOC_IA64_DIR64MSB:
+         new = BFD_RELOC_IA64_DTPREL64MSB; break;
+       case BFD_RELOC_IA64_DIR64LSB:
+         new = BFD_RELOC_IA64_DTPREL64LSB; break;
+       case BFD_RELOC_IA64_IMM14:
+         new = BFD_RELOC_IA64_DTPREL14; break;
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_DTPREL22; break;
+       case BFD_RELOC_IA64_IMM64:
+         new = BFD_RELOC_IA64_DTPREL64I; break;
+       default:
+         type = "DTPREL"; break;
        }
       break;
+
+    case FUNC_LT_DTP_RELATIVE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_LTOFF_DTPREL22; break;
+       default:
+         type = "LTOFF_DTPREL"; break;
+       }
+      break;
+
+    case FUNC_IPLT_RELOC:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IPLTMSB:    return r_type;
+       case BFD_RELOC_IA64_IPLTLSB:    return r_type;
+       default:                        type = "IPLT"; break;
+       }
+      break;
+
     default:
       abort ();
     }
-  /* Hmmmm.  Should this ever occur?  */
+
   if (new)
     return new;
   else
-    return r_type;
+    {
+      int width;
+
+      if (!type)
+       abort ();
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_DIR32MSB: width = 32; suffix = "MSB"; break;
+       case BFD_RELOC_IA64_DIR32LSB: width = 32; suffix = "LSB"; break;
+       case BFD_RELOC_IA64_DIR64MSB: width = 64; suffix = "MSB"; break;
+       case BFD_RELOC_IA64_DIR64LSB: width = 64; suffix = "LSB"; break;
+       case BFD_RELOC_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
@@ -9951,8 +11277,6 @@ ia64_validate_fix (fix)
     default:
       break;
     }
-
-  return;
 }
 
 static void
@@ -10019,63 +11343,67 @@ fix_insn (fix, odesc, value)
 
    If fixp->fx_addsy is non-NULL, we'll have to generate a reloc entry
    (if possible).  */
-int
-md_apply_fix3 (fix, valuep, seg)
+
+void
+md_apply_fix3 (fix, valP, seg)
      fixS *fix;
-     valueT *valuep;
+     valueT *valP;
      segT seg ATTRIBUTE_UNUSED;
 {
   char *fixpos;
-  valueT value = *valuep;
-  int adjust = 0;
+  valueT value = *valP;
 
   fixpos = fix->fx_frag->fr_literal + fix->fx_where;
 
   if (fix->fx_pcrel)
     {
-      switch (fix->fx_r_type)
-       {
-       case BFD_RELOC_IA64_DIR32MSB:
-         fix->fx_r_type = BFD_RELOC_IA64_PCREL32MSB;
-         adjust = 1;
-         break;
-
-       case BFD_RELOC_IA64_DIR32LSB:
-         fix->fx_r_type = BFD_RELOC_IA64_PCREL32LSB;
-         adjust = 1;
-         break;
-
-       case BFD_RELOC_IA64_DIR64MSB:
-         fix->fx_r_type = BFD_RELOC_IA64_PCREL64MSB;
-         adjust = 1;
-         break;
-
-       case BFD_RELOC_IA64_DIR64LSB:
-         fix->fx_r_type = BFD_RELOC_IA64_PCREL64LSB;
-         adjust = 1;
-         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_r_type == (int) BFD_RELOC_UNUSED)
+      switch (fix->fx_r_type)
        {
+       case BFD_RELOC_UNUSED:
          /* This must be a TAG13 or TAG13b operand.  There are no external
             relocs defined for them, so we must give an error.  */
          as_bad_where (fix->fx_file, fix->fx_line,
                        "%s must have a constant value",
                        elf64_ia64_operands[fix->tc_fix_data.opnd].desc);
          fix->fx_done = 1;
-         return 1;
-       }
+         return;
 
-      /* ??? This is a hack copied from tc-i386.c to make PCREL relocs
-        work.  There should be a better way to handle this.  */
-      if (adjust)
-       fix->fx_offset += fix->fx_where + fix->fx_frag->fr_address;
+       case BFD_RELOC_IA64_TPREL14:
+       case BFD_RELOC_IA64_TPREL22:
+       case BFD_RELOC_IA64_TPREL64I:
+       case BFD_RELOC_IA64_LTOFF_TPREL22:
+       case BFD_RELOC_IA64_LTOFF_DTPMOD22:
+       case BFD_RELOC_IA64_DTPREL14:
+       case BFD_RELOC_IA64_DTPREL22:
+       case BFD_RELOC_IA64_DTPREL64I:
+       case BFD_RELOC_IA64_LTOFF_DTPREL22:
+         S_SET_THREAD_LOCAL (fix->fx_addsy);
+         break;
+
+       default:
+         break;
+       }
     }
   else if (fix->tc_fix_data.opnd == IA64_OPND_NIL)
     {
@@ -10084,15 +11412,12 @@ md_apply_fix3 (fix, valuep, seg)
       else
        number_to_chars_littleendian (fixpos, value, fix->fx_size);
       fix->fx_done = 1;
-      return 1;
     }
   else
     {
       fix_insn (fix, elf64_ia64_operands + fix->tc_fix_data.opnd, value);
       fix->fx_done = 1;
-      return 1;
     }
-  return 1;
 }
 
 /* Generate the BFD reloc to be stuck in the object file from the
@@ -10135,7 +11460,6 @@ md_atof (type, lit, size)
      int *size;
 {
   LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  LITTLENUM_TYPE *word;
   char *t;
   int prec;
 
@@ -10170,26 +11494,19 @@ md_atof (type, lit, size)
   t = atof_ieee (input_line_pointer, type, words);
   if (t)
     input_line_pointer = t;
-  *size = prec * sizeof (LITTLENUM_TYPE);
 
-  for (word = words + prec - 1; prec--;)
+  (*ia64_float_to_chars) (lit, words, prec);
+
+  if (type == 'X')
     {
-      md_number_to_chars (lit, (long) (*word--), sizeof (LITTLENUM_TYPE));
-      lit += sizeof (LITTLENUM_TYPE);
+      /* It is 10 byte floating point with 6 byte padding.  */
+      memset (&lit [10], 0, 6);
+      *size = 8 * sizeof (LITTLENUM_TYPE);
     }
-  return 0;
-}
-
-/* Round up a section's size to the appropriate boundary.  */
-valueT
-md_section_align (seg, size)
-     segT seg;
-     valueT size;
-{
-  int align = bfd_get_section_alignment (stdoutput, seg);
-  valueT mask = ((valueT) 1 << align) - 1;
+  else
+    *size = prec * sizeof (LITTLENUM_TYPE);
 
-  return (size + mask) & ~mask;
+  return 0;
 }
 
 /* Handle ia64 specific semantics of the align directive.  */
@@ -10212,23 +11529,39 @@ void
 ia64_handle_align (fragp)
      fragS *fragp;
 {
-  /* Use mfi bundle of nops with no stop bits.  */
-  static const unsigned char be_nop[]
-    = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
-       0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c};
-  static const unsigned char le_nop[]
-    = { 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-       0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
-
   int bytes;
   char *p;
+  const unsigned char *nop;
 
   if (fragp->fr_type != rs_align_code)
     return;
 
+  /* Check if this frag has to end with a stop bit.  */
+  nop = fragp->tc_frag_data ? le_nop_stop : le_nop;
+
   bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
   p = fragp->fr_literal + fragp->fr_fix;
 
+  /* If no paddings are needed, we check if we need a stop bit.  */ 
+  if (!bytes && fragp->tc_frag_data)
+    {
+      if (fragp->fr_fix < 16)
+#if 1
+       /* FIXME: It won't work with
+          .align 16
+          alloc r32=ar.pfs,1,2,4,0
+        */
+       ;
+#else
+       as_bad_where (fragp->fr_file, fragp->fr_line,
+                     _("Can't add stop bit to mark end of instruction group"));
+#endif
+      else
+       /* Bundles are always in little-endian byte order. Make sure
+          the previous bundle has the stop bit.  */
+       *(p - 16) |= 1;
+    }
+
   /* Make sure we are on a 16-byte boundary, in case someone has been
      putting data into a text section.  */
   if (bytes & 15)
@@ -10240,6 +11573,218 @@ ia64_handle_align (fragp)
       fragp->fr_fix += fix;
     }
 
-  memcpy (p, (target_big_endian ? be_nop : le_nop), 16);
+  /* Instruction bundles are always little-endian.  */
+  memcpy (p, nop, 16);
   fragp->fr_var = 16;
 }
+
+static void
+ia64_float_to_chars_bigendian (char *lit, LITTLENUM_TYPE *words,
+                              int prec)
+{
+  while (prec--)
+    {
+      number_to_chars_bigendian (lit, (long) (*words++),
+                                sizeof (LITTLENUM_TYPE));
+      lit += sizeof (LITTLENUM_TYPE);
+    }
+}
+
+static void
+ia64_float_to_chars_littleendian (char *lit, LITTLENUM_TYPE *words,
+                                 int prec)
+{
+  while (prec--)
+    {
+      number_to_chars_littleendian (lit, (long) (words[prec]),
+                                   sizeof (LITTLENUM_TYPE));
+      lit += sizeof (LITTLENUM_TYPE);
+    }
+}
+
+void
+ia64_elf_section_change_hook  (void)
+{
+  if (elf_section_type (now_seg) == SHT_IA_64_UNWIND
+      && elf_linked_to_section (now_seg) == NULL)
+    elf_linked_to_section (now_seg) = text_section;
+  dot_byteorder (-1);
+}
+
+/* Check if a label should be made global.  */
+void
+ia64_check_label (symbolS *label)
+{
+  if (*input_line_pointer == ':')
+    {
+      S_SET_EXTERNAL (label);
+      input_line_pointer++;
+    }
+}
+
+/* Used to remember where .alias and .secalias directives are seen. We
+   will rename symbol and section names when we are about to output
+   the relocatable file.  */
+struct alias
+{
+  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.  */
+};
+
+/* Called for .alias and .secalias directives. If SECTION is 1, it is
+   .secalias. Otherwise, it is .alias.  */
+static void
+dot_alias (int section)
+{
+  char *name, *alias;
+  char delim;
+  char *end_name;
+  int len;
+  const char *error_string;
+  struct alias *h;
+  const char *a;
+  struct hash_control *ahash, *nhash;
+  const char *kind;
+
+  name = input_line_pointer;
+  delim = get_symbol_end ();
+  end_name = input_line_pointer;
+  *end_name = delim;
+
+  if (name == end_name)
+    {
+      as_bad (_("expected symbol name"));
+      discard_rest_of_line ();
+      return;
+    }
+
+  SKIP_WHITESPACE ();
+
+  if (*input_line_pointer != ',')
+    {
+      *end_name = 0;
+      as_bad (_("expected comma after \"%s\""), name);
+      *end_name = delim;
+      ignore_rest_of_line ();
+      return;
+    }
+
+  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.  */
+  alias = demand_copy_C_string (&len);
+  if (alias == NULL)
+    {
+      ignore_rest_of_line ();
+      return;
+    }
+
+  /* Make a copy of name string.  */
+  len = strlen (name) + 1;
+  obstack_grow (&notes, name, len);
+  name = obstack_finish (&notes);
+
+  if (section)
+    {
+      kind = "section";
+      ahash = secalias_hash;
+      nhash = secalias_name_hash;
+    }
+  else
+    {
+      kind = "symbol";
+      ahash = alias_hash;
+      nhash = alias_name_hash;
+    }
+
+  /* Check if alias has been used before.  */
+  h = (struct alias *) hash_find (ahash, alias);
+  if (h)
+    {
+      if (strcmp (h->name, name))
+       as_bad (_("`%s' is already the alias of %s `%s'"),
+               alias, kind, h->name);
+      goto out;
+    }
+
+  /* Check if name already has an alias.  */
+  a = (const char *) hash_find (nhash, name);
+  if (a)
+    {
+      if (strcmp (a, alias))
+       as_bad (_("%s `%s' already has an alias `%s'"), kind, name, a);
+      goto out;
+    }
+
+  h = (struct alias *) xmalloc (sizeof (struct alias));
+  as_where (&h->file, &h->line);
+  h->name = name;
+  
+  error_string = hash_jam (ahash, alias, (PTR) h);
+  if (error_string)
+    {
+      as_fatal (_("inserting \"%s\" into %s alias hash table failed: %s"),
+               alias, kind, error_string);
+      goto out;
+    }
+
+  error_string = hash_jam (nhash, name, (PTR) alias);
+  if (error_string)
+    {
+      as_fatal (_("inserting \"%s\" into %s name hash table failed: %s"),
+               alias, kind, error_string);
+out:
+      obstack_free (&notes, name);
+      obstack_free (&notes, alias);
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* It renames the original symbol name to its alias.  */
+static void
+do_alias (const char *alias, PTR value)
+{
+  struct alias *h = (struct alias *) value;
+  symbolS *sym = symbol_find (h->name);
+
+  if (sym == NULL)
+    as_warn_where (h->file, h->line,
+                  _("symbol `%s' aliased to `%s' is not used"),
+                  h->name, alias);
+    else
+      S_SET_NAME (sym, (char *) alias);
+}
+
+/* Called from write_object_file.  */
+void
+ia64_adjust_symtab (void)
+{
+  hash_traverse (alias_hash, do_alias);
+}
+
+/* It renames the original section name to its alias.  */
+static void
+do_secalias (const char *alias, PTR value)
+{
+  struct alias *h = (struct alias *) value;
+  segT sec = bfd_get_section_by_name (stdoutput, h->name);
+
+  if (sec == NULL)
+    as_warn_where (h->file, h->line,
+                  _("section `%s' aliased to `%s' is not used"),
+                  h->name, alias);
+  else
+    sec->name = alias;
+}
+
+/* Called from write_object_file.  */
+void
+ia64_frob_file (void)
+{
+  hash_traverse (secalias_hash, do_secalias);
+}
This page took 0.089836 seconds and 4 git commands to generate.