public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] ia64: unwind directive handling
@ 2005-05-23 10:19 Jan Beulich
  2005-06-10  0:07 ` James E Wilson
  0 siblings, 1 reply; 8+ messages in thread
From: Jan Beulich @ 2005-05-23 10:19 UTC (permalink / raw)
  To: binutils

[-- Attachment #1: Type: text/plain, Size: 4470 bytes --]

The main purpose of this patch is to add parser level support for the
optional tags various of the unwind directives permit and to make '}' a
statement separator as is done in ias. Along with that it consolidates
some redundant functionality, tightens operand checking for many unwind
directives, and fixes some other bugs.

Separating these into distinct patches would be very cumbersome, so I hope
applying this in one piece is acceptable (various pieces have already been
broken out and submitted previously).

There is one slightly odd thing in here: .prologue doesn't (as per the Intel
spec) allow an immediate as the second operand, but gcc for whatever reason
uses exactly that non-standard form. Thus the warning of this form being
deprecated had to be masked by an option not on by default; I chose 
unwind_check_error here rather than adding yet another controlling command
line option.

The actual handling of tags used with unwind directives still needs to be
implemented. Similarly, proper handling of .save.x directives with more
than one bit set in their operands needs to be done.

Built and tested on ia64-unknown-linux-gnu.

Jan

binutils/
2005-05-23  Jan Beulich  <jbeulich@novell.com>

	* unwind-ia64.c (unw_decode_p7_p10): Add psp_psprel to P8 handling.

gas/
2005-05-23  Jan Beulich  <jbeulich@novell.com>

	* config/tc-ia64.h (unw_record_type): Add psp_psprel.
	* config/tc-ia64.c (line_separator_chars): Add '}'.
	(output_P8_format): Handle psp_psprel.
	(output_psp_psprel): New.
	(output_spill_psprel, output_spill_psprel_p): Combine.
	(output_spill_sprel, output_spill_sprel_p): Combine.
	(output_spill_reg, output_spill_regp_p): Combine.
	(process_one_record): Handle psp_psprel.
	(parse_predicate_and_operand): New.
	(convert_expr_to_ab_reg): Two new parameters. Return void. Always
	initialize output values. Emit diagnostic case here.
	(convert_expr_to_xy_reg): Likewise. Don't allow r0, f0, and f1.
	(add_unwind_entry): New second parameter. Parse optional tag, emit
	warning about further support for it otherwise being missing. Check
	end-of-line when requested.
	(dot_fframe): Clear operand when wrong. Allow tag.
	(dot_vframe): Likewise.
	(dot_vframesp): Likewise.
	(dot_vframepsp): Likewise.
	(dot_altrp): Likewise.
	(dot_save): Likewise. Let default case fall through to also go
	through add_unwind_entry.
	(dot_savemem): Likewise.
	(dot_restore): Don't return when wrong operand. Allow tag.
	(dot_spillreg, dot_spillreg_p): Combine. Simplify by using
	parse_predicate_and_operand and the new arguments to
	convert_expr_to_ab_reg and convert_expr_to_xy_reg. Don't return
	when wrong operand. Allow tag.
	(dot_restorereg, dot_restorereg_p): Likewise.
	(dot_spillmem, dot_spillmem_p): Likewise.
	(dot_saveg): Clear operand when wrong. Perform tighter operand
	checks. Allow tag.
	(dot_savef): Likewise.
	(dot_saveb): Likewise.
	(dot_savegf): Likewise.
	(dot_spill): Remove end-of-line check. 	Combine. Simplify by using
	parse_predicate_and_operand and the new arguments to
	convert_expr_to_ab_reg and convert_expr_to_xy_reg. Don't return
	when wrong operand. Allow tag.
	(popcount): New.
	(dot_label_state): Don't return when wrong operand.
	(dot_copy_state): Likewise.
	(dot_unwabi): Likewise. Check if in prologue.
	(dot_body): Don't call demand_empty_rest_of_line.
	(dot_prologue): Type of mask and grsave is unsigned. Perform tighter
	operand checks.
	(md_pseudo_table): Also use dot_restorereg for .restorereg.p. Also
	use dot_spillreg for .spillreg.p. Also use dot_spillmem for
	.spillpsp.p and .spillsp.p.
	(parse_operand): New second parameter. Don't deal with '}' here
	anymore. Don't advance past end-of-line.
	(parse_operands): Pass second argument to parse_operand.
	(ia64_start_line): Prevent out-of-bounds access through
	input_line_pointer. Deal with '}' here.
	(ia64_unrecognized_line): Don't deal with '}' here.
	(dot_alias): Use ignore_rest_of_line not its deprecated alias
	discard_rest_of_line.

gas/testsuite/
2005-05-23  Jan Beulich  <jbeulich@novell.com>

	* gas/ia64/group-2.s: Use register as second operand of .prologue.
	* gas/ia64/unwind-err.s: Add check for .vframesp and .vframepsp.
	* gas/ia64/unwind-err.l: Adjust.
	* gas/ia64/strange.[sd]: New.
	* gas/ia64/unwind-bad.[sl]: New.
	* gas/ia64/unwind-ok.[sd]: New.
	* gas/ia64/ia64.exp: Run new tests.

(actual patch too large for inlining; attached)

[-- Attachment #2: binutils-mainline-ia64-unwind.patch --]
[-- Type: text/plain, Size: 78451 bytes --]

The main purpose of this patch is to add parser level support for the
optional tags various of the unwind directives permit and to make '}' a
statement separator as is done in ias. Along with that it consolidates
some redundant functionality, tightens operand checking for many unwind
directives, and fixes some other bugs.

Separating these into distinct patches would be very cumbersome, so I hope
applying this in one piece is acceptable (various pieces have already been
broken out and submitted previously).

There is one slightly odd thing in here: .prologue doesn't (as per the Intel
spec) allow an immediate as the second operand, but gcc for whatever reason
uses exactly that non-standard form. Thus the warning of this form being
deprecated had to be masked by an option not on by default; I chose 
unwind_check_error here rather than adding yet another controlling command
line option.

The actual handling of tags used with unwind directives still needs to be
implemented. Similarly, proper handling of .save.x directives with more
than one bit set in their operands needs to be done.

Built and tested on ia64-unknown-linux-gnu.

Jan

binutils/
2005-05-23  Jan Beulich  <jbeulich@novell.com>

	* unwind-ia64.c (unw_decode_p7_p10): Add psp_psprel to P8 handling.

gas/
2005-05-23  Jan Beulich  <jbeulich@novell.com>

	* config/tc-ia64.h (unw_record_type): Add psp_psprel.
	* config/tc-ia64.c (line_separator_chars): Add '}'.
	(output_P8_format): Handle psp_psprel.
	(output_psp_psprel): New.
	(output_spill_psprel, output_spill_psprel_p): Combine.
	(output_spill_sprel, output_spill_sprel_p): Combine.
	(output_spill_reg, output_spill_regp_p): Combine.
	(process_one_record): Handle psp_psprel.
	(parse_predicate_and_operand): New.
	(convert_expr_to_ab_reg): Two new parameters. Return void. Always
	initialize output values. Emit diagnostic case here.
	(convert_expr_to_xy_reg): Likewise. Don't allow r0, f0, and f1.
	(add_unwind_entry): New second parameter. Parse optional tag, emit
	warning about further support for it otherwise being missing. Check
	end-of-line when requested.
	(dot_fframe): Clear operand when wrong. Allow tag.
	(dot_vframe): Likewise.
	(dot_vframesp): Likewise.
	(dot_vframepsp): Likewise.
	(dot_altrp): Likewise.
	(dot_save): Likewise. Let default case fall through to also go
	through add_unwind_entry.
	(dot_savemem): Likewise.
	(dot_restore): Don't return when wrong operand. Allow tag.
	(dot_spillreg, dot_spillreg_p): Combine. Simplify by using
	parse_predicate_and_operand and the new arguments to
	convert_expr_to_ab_reg and convert_expr_to_xy_reg. Don't return
	when wrong operand. Allow tag.
	(dot_restorereg, dot_restorereg_p): Likewise.
	(dot_spillmem, dot_spillmem_p): Likewise.
	(dot_saveg): Clear operand when wrong. Perform tighter operand
	checks. Allow tag.
	(dot_savef): Likewise.
	(dot_saveb): Likewise.
	(dot_savegf): Likewise.
	(dot_spill): Remove end-of-line check. 	Combine. Simplify by using
	parse_predicate_and_operand and the new arguments to
	convert_expr_to_ab_reg and convert_expr_to_xy_reg. Don't return
	when wrong operand. Allow tag.
	(popcount): New.
	(dot_label_state): Don't return when wrong operand.
	(dot_copy_state): Likewise.
	(dot_unwabi): Likewise. Check if in prologue.
	(dot_body): Don't call demand_empty_rest_of_line.
	(dot_prologue): Type of mask and grsave is unsigned. Perform tighter
	operand checks.
	(md_pseudo_table): Also use dot_restorereg for .restorereg.p. Also
	use dot_spillreg for .spillreg.p. Also use dot_spillmem for
	.spillpsp.p and .spillsp.p.
	(parse_operand): New second parameter. Don't deal with '}' here
	anymore. Don't advance past end-of-line.
	(parse_operands): Pass second argument to parse_operand.
	(ia64_start_line): Prevent out-of-bounds access through
	input_line_pointer. Deal with '}' here.
	(ia64_unrecognized_line): Don't deal with '}' here.
	(dot_alias): Use ignore_rest_of_line not its deprecated alias
	discard_rest_of_line.

gas/testsuite/
2005-05-23  Jan Beulich  <jbeulich@novell.com>

	* gas/ia64/group-2.s: Use register as second operand of .prologue.
	* gas/ia64/unwind-err.s: Add check for .vframesp and .vframepsp.
	* gas/ia64/unwind-err.l: Adjust.
	* gas/ia64/strange.[sd]: New.
	* gas/ia64/unwind-bad.[sl]: New.
	* gas/ia64/unwind-ok.[sd]: New.
	* gas/ia64/ia64.exp: Run new tests.

--- /home/jbeulich/src/binutils/mainline/2005-05-18/binutils/unwind-ia64.c	2005-05-09 08:31:30.000000000 +0200
+++ 2005-05-18/binutils/unwind-ia64.c	2005-05-20 16:41:30.000000000 +0200
@@ -883,6 +883,9 @@ unw_decode_p7_p10 (const unsigned char *
 	    t = unw_decode_uleb128 (&dp);
 	    switch (r)
 	      {
+	      case 0:
+		UNW_DEC_REG_PSPREL ("P8", UNW_REG_PSP, t, arg);
+		break;
 	      case 1:
 		UNW_DEC_REG_SPREL ("P8", UNW_REG_RP, t, arg);
 		break;
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/config/tc-ia64.c	2005-05-09 08:31:38.000000000 +0200
+++ 2005-05-18/gas/config/tc-ia64.c	2005-05-23 11:47:24.643235655 +0200
@@ -193,7 +193,7 @@ const char line_comment_chars[] = "#";
 
 /* Characters which may be used to separate multiple commands on a
    single line.  */
-const char line_separator_chars[] = ";";
+const char line_separator_chars[] = ";}";
 
 /* Characters which are used to indicate an exponent in a floating
    point number.  */
@@ -762,7 +760,6 @@ static void dot_vframepsp PARAMS ((int))
 static void dot_save PARAMS ((int));
 static void dot_restore PARAMS ((int));
 static void dot_restorereg PARAMS ((int));
-static void dot_restorereg_p PARAMS ((int));
 static void dot_handlerdata  PARAMS ((int));
 static void dot_unwentry PARAMS ((int));
 static void dot_altrp PARAMS ((int));
@@ -774,8 +771,6 @@ static void dot_savegf PARAMS ((int));
 static void dot_spill PARAMS ((int));
 static void dot_spillreg PARAMS ((int));
 static void dot_spillmem PARAMS ((int));
-static void dot_spillreg_p PARAMS ((int));
-static void dot_spillmem_p PARAMS ((int));
 static void dot_label_state PARAMS ((int));
 static void dot_copy_state PARAMS ((int));
 static void dot_unwabi PARAMS ((int));
@@ -805,14 +800,14 @@ static void dot_serialize PARAMS ((int))
 static void dot_dv_mode PARAMS ((int));
 static void dot_entry PARAMS ((int));
 static void dot_mem_offset PARAMS ((int));
-static void add_unwind_entry PARAMS((unw_rec_list *ptr));
+static void add_unwind_entry PARAMS((unw_rec_list *, int));
 static symbolS *declare_register PARAMS ((const char *name, int regnum));
 static void declare_register_set PARAMS ((const char *, int, int));
 static unsigned int operand_width PARAMS ((enum ia64_opnd));
 static enum operand_match_result operand_match PARAMS ((const struct ia64_opcode *idesc,
 							int index,
 							expressionS *e));
-static int parse_operand PARAMS ((expressionS *e));
+static int parse_operand PARAMS ((expressionS *, int));
 static struct ia64_opcode * parse_operands PARAMS ((struct ia64_opcode *));
 static void build_insn PARAMS ((struct slot *, bfd_vma *));
 static void emit_one_bundle PARAMS ((void));
@@ -879,6 +874,7 @@ static unw_rec_list *output_body PARAMS 
 static unw_rec_list *output_mem_stack_f PARAMS ((unsigned int));
 static unw_rec_list *output_mem_stack_v PARAMS ((void));
 static unw_rec_list *output_psp_gr PARAMS ((unsigned int));
+static unw_rec_list *output_psp_psprel PARAMS ((unsigned int));
 static unw_rec_list *output_psp_sprel PARAMS ((unsigned int));
 static unw_rec_list *output_rp_when PARAMS ((void));
 static unw_rec_list *output_rp_gr PARAMS ((unsigned int));
@@ -933,15 +929,11 @@ static unw_rec_list *output_unwabi PARAM
 static unw_rec_list *output_epilogue PARAMS ((unsigned long));
 static unw_rec_list *output_label_state PARAMS ((unsigned long));
 static unw_rec_list *output_copy_state PARAMS ((unsigned long));
-static unw_rec_list *output_spill_psprel PARAMS ((unsigned int, unsigned int, unsigned int));
-static unw_rec_list *output_spill_sprel PARAMS ((unsigned int, unsigned int, unsigned int));
-static unw_rec_list *output_spill_psprel_p PARAMS ((unsigned int, unsigned int, unsigned int,
+static unw_rec_list *output_spill_psprel PARAMS ((unsigned int, unsigned int, unsigned int,
 						    unsigned int));
-static unw_rec_list *output_spill_sprel_p PARAMS ((unsigned int, unsigned int, unsigned int,
+static unw_rec_list *output_spill_sprel PARAMS ((unsigned int, unsigned int, unsigned int,
 						   unsigned int));
 static unw_rec_list *output_spill_reg PARAMS ((unsigned int, unsigned int, unsigned int,
-					       unsigned int));
-static unw_rec_list *output_spill_reg_p PARAMS ((unsigned int, unsigned int, unsigned int,
 						 unsigned int, unsigned int));
 static void process_one_record PARAMS ((unw_rec_list *, vbyte_func));
 static void process_unw_records PARAMS ((unw_rec_list *, vbyte_func));
@@ -952,8 +944,9 @@ static unsigned long slot_index PARAMS (
 					 int));
 static unw_rec_list *optimize_unw_records PARAMS ((unw_rec_list *));
 static void fixup_unw_records PARAMS ((unw_rec_list *, int));
-static int convert_expr_to_ab_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
-static int convert_expr_to_xy_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
+static int parse_predicate_and_operand PARAMS ((expressionS *, unsigned *, const char *));
+static void convert_expr_to_ab_reg PARAMS ((const expressionS *, unsigned int *, unsigned int *, const char *, int));
+static void convert_expr_to_xy_reg PARAMS ((const expressionS *, unsigned int *, unsigned int *, const char *, int));
 static unsigned int get_saved_prologue_count PARAMS ((unsigned long));
 static void save_prologue_count PARAMS ((unsigned long, unsigned int));
 static void free_saved_prologue_counts PARAMS ((void));
@@ -1471,6 +1464,9 @@ output_P8_format (f, rtype, t)
   bytes[0] = UNW_P8;
   switch (rtype)
     {
+    case psp_psprel:
+      r = 0;
+      break;
     case rp_sprel:
       r = 1;
       break;
@@ -1818,6 +1812,15 @@ output_psp_gr (gr)
 }
 
 static unw_rec_list *
+output_psp_psprel (offset)
+     unsigned int offset;
+{
+  unw_rec_list *ptr = alloc_record (psp_psprel);
+  ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
+  return ptr;
+}
+
+static unw_rec_list *
 output_psp_sprel (offset)
      unsigned int offset;
 {
@@ -2286,39 +2289,13 @@ output_copy_state (unsigned long label)
 }
 
 static unw_rec_list *
-output_spill_psprel (ab, reg, offset)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int offset;
-{
-  unw_rec_list *ptr = alloc_record (spill_psprel);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_sprel (ab, reg, offset)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int offset;
-{
-  unw_rec_list *ptr = alloc_record (spill_sprel);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.spoff = offset / 4;
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_psprel_p (ab, reg, offset, predicate)
+output_spill_psprel (ab, reg, offset, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int offset;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_psprel_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_psprel_p : spill_psprel);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
   ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
@@ -2327,13 +2304,13 @@ output_spill_psprel_p (ab, reg, offset, 
 }
 
 static unw_rec_list *
-output_spill_sprel_p (ab, reg, offset, predicate)
+output_spill_sprel (ab, reg, offset, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int offset;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_sprel_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_sprel_p : spill_sprel);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
   ptr->r.record.x.spoff = offset / 4;
@@ -2342,29 +2319,14 @@ output_spill_sprel_p (ab, reg, offset, p
 }
 
 static unw_rec_list *
-output_spill_reg (ab, reg, targ_reg, xy)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int targ_reg;
-     unsigned int xy;
-{
-  unw_rec_list *ptr = alloc_record (spill_reg);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.treg = targ_reg;
-  ptr->r.record.x.xy = xy;
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_reg_p (ab, reg, targ_reg, xy, predicate)
+output_spill_reg (ab, reg, targ_reg, xy, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int targ_reg;
      unsigned int xy;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_reg_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_reg_p : spill_reg);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
   ptr->r.record.x.treg = targ_reg;
@@ -2472,6 +2434,9 @@ process_one_record (ptr, f)
     case spill_base:
       output_P7_format (f, ptr->r.type, ptr->r.record.p.pspoff, 0);
       break;
+    case psp_psprel:
+      output_P8_format (f, ptr->r.type, ptr->r.record.p.pspoff);
+      break;
     case rp_sprel:
     case pfs_sprel:
     case preds_sprel:
@@ -2978,17 +2943,43 @@ ia64_convert_frag (fragS *frag)
 }
 
 static int
-convert_expr_to_ab_reg (e, ab, regp)
-     expressionS *e;
+parse_predicate_and_operand (e, qp, po)
+     expressionS * e;
+     unsigned * qp;
+     const char * po;
+{
+  int sep = parse_operand (e, ',');
+
+  *qp = e->X_add_number - REG_P;
+  if (e->X_op != O_register || *qp > 63)
+    {
+      as_bad ("First operand to .%s must be a predicate", po);
+      *qp = 0;
+    }
+  else if (*qp == 0)
+    as_warn ("Pointless use of p0 as first operand to .%s", po);
+  if (sep == ',')
+    sep = parse_operand (e, ',');
+  else
+    e->X_op = O_absent;
+  return sep;
+}
+
+static void
+convert_expr_to_ab_reg (e, ab, regp, po, n)
+     const expressionS *e;
      unsigned int *ab;
      unsigned int *regp;
+     const char * po;
+     int n;
 {
-  unsigned int reg;
+  unsigned int reg = e->X_add_number;
+
+  *ab = *regp = 0; /* Anything valid is good here.  */
 
   if (e->X_op != O_register)
-    return 0;
+    reg = REG_GR; /* Anything invalid is good here.  */
 
-  reg = e->X_add_number;
   if (reg >= (REG_GR + 4) && reg <= (REG_GR + 7))
     {
       *ab = 0;
@@ -3023,31 +3014,33 @@ convert_expr_to_ab_reg (e, ab, regp)
 	case REG_AR + AR_LC:	*regp = 10; break;
 
 	default:
-	  return 0;
+	  as_bad ("Operand %d to .%s must be a preserved register", n, po);
+	  break;
 	}
     }
-  return 1;
 }
 
-static int
-convert_expr_to_xy_reg (e, xy, regp)
-     expressionS *e;
+static void
+convert_expr_to_xy_reg (e, xy, regp, po, n)
+     const expressionS *e;
      unsigned int *xy;
      unsigned int *regp;
+     const char * po;
+     int n;
 {
-  unsigned int reg;
+  unsigned int reg = e->X_add_number;
 
-  if (e->X_op != O_register)
-    return 0;
+  *xy = *regp = 0; /* Anything valid is good here.  */
 
-  reg = e->X_add_number;
+  if (e->X_op != O_register)
+    reg = REG_GR; /* Anything invalid is good here.  */
 
-  if (/* reg >= REG_GR && */ reg <= (REG_GR + 127))
+  if (reg >= (REG_GR + 1) && reg <= (REG_GR + 127))
     {
       *xy = 0;
       *regp = reg - REG_GR;
     }
-  else if (reg >= REG_FR && reg <= (REG_FR + 127))
+  else if (reg >= (REG_FR + 2) && reg <= (REG_FR + 127))
     {
       *xy = 1;
       *regp = reg - REG_FR;
@@ -3058,8 +3051,7 @@ convert_expr_to_xy_reg (e, xy, regp)
       *regp = reg - REG_BR;
     }
   else
-    return -1;
-  return 1;
+    as_bad ("Operand %d to .%s must be a writable register", n, po);
 }
 
 static void
@@ -3185,8 +3180,9 @@ in_body (const char *directive)
 }
 
 static void
-add_unwind_entry (ptr)
+add_unwind_entry (ptr, sep)
      unw_rec_list *ptr;
+     int sep;
 {
   if (unwind.tail)
     unwind.tail->next = ptr;
@@ -3197,6 +3193,28 @@ add_unwind_entry (ptr)
   /* The current entry can in fact be a chain of unwind entries.  */
   if (unwind.current_entry == NULL)
     unwind.current_entry = ptr;
+
+  if (sep == ',')
+    {
+      /* Parse a tag permitted for the current directive.  */
+      int ch;
+
+      SKIP_WHITESPACE ();
+      ch = get_symbol_end ();
+      /* FIXME: For now, just issue a warning that this isn't implemented.  */
+      {
+	static int warned;
+
+	if (!warned)
+	  {
+	    warned = 1;
+	    as_warn ("Tags on unwind pseudo-ops aren't supported, yet");
+	  }
+      }
+      *input_line_pointer = ch;
+    }
+  if (sep != NOT_A_CHAR)
+    demand_empty_rest_of_line ();
 }
 
 static void
@@ -3204,16 +3222,19 @@ dot_fframe (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
+  int sep;
 
   if (!in_prologue ("fframe"))
     return;
 
-  parse_operand (&e);
+  sep = parse_operand (&e, ',');
 
   if (e.X_op != O_constant)
-    as_bad ("Operand to .fframe must be a constant");
-  else
-    add_unwind_entry (output_mem_stack_f (e.X_add_number));
+    {
+      as_bad ("First operand to .fframe must be a constant");
+      e.X_add_number = 0;
+    }
+  add_unwind_entry (output_mem_stack_f (e.X_add_number), sep);
 }
 
 static void
@@ -3222,20 +3243,21 @@ dot_vframe (dummy)
 {
   expressionS e;
   unsigned reg;
+  int sep;
 
   if (!in_prologue ("vframe"))
     return;
 
-  parse_operand (&e);
+  sep = parse_operand (&e, ',');
   reg = e.X_add_number - REG_GR;
-  if (e.X_op == O_register && reg < 128)
+  if (e.X_op != O_register || reg > 127)
     {
-      add_unwind_entry (output_mem_stack_v ());
-      if (! (unwind.prologue_mask & 2))
-	add_unwind_entry (output_psp_gr (reg));
+      as_bad ("First operand to .vframe must be a general register");
+      reg = 0;
     }
-  else
-    as_bad ("First operand to .vframe must be a general register");
+  add_unwind_entry (output_mem_stack_v (), sep);
+  if (! (unwind.prologue_mask & 2))
+    add_unwind_entry (output_psp_gr (reg), NOT_A_CHAR);
 }
 
 static void
@@ -3243,18 +3265,19 @@ dot_vframesp (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
+  int sep;
 
   if (!in_prologue ("vframesp"))
     return;
 
-  parse_operand (&e);
-  if (e.X_op == O_constant)
+  sep = parse_operand (&e, ',');
+  if (e.X_op != O_constant)
     {
-      add_unwind_entry (output_mem_stack_v ());
-      add_unwind_entry (output_psp_sprel (e.X_add_number));
+      as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
+      e.X_add_number = 0;
     }
-  else
-    as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
+  add_unwind_entry (output_mem_stack_v (), sep);
+  add_unwind_entry (output_psp_sprel (e.X_add_number), NOT_A_CHAR);
 }
 
 static void
@@ -3262,18 +3285,19 @@ dot_vframepsp (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
+  int sep;
 
   if (!in_prologue ("vframepsp"))
     return;
 
-  parse_operand (&e);
-  if (e.X_op == O_constant)
+  sep = parse_operand (&e, ',');
+  if (e.X_op != O_constant)
     {
-      add_unwind_entry (output_mem_stack_v ());
-      add_unwind_entry (output_psp_sprel (e.X_add_number));
+      as_bad ("Operand to .vframepsp must be a constant (psp-relative offset)");
+      e.X_add_number = 0;
     }
-  else
-    as_bad ("Operand to .vframepsp must be a constant (psp-relative offset)");
+  add_unwind_entry (output_mem_stack_v (), sep);
+  add_unwind_entry (output_psp_psprel (e.X_add_number), NOT_A_CHAR);
 }
 
 static void
@@ -3281,106 +3305,105 @@ dot_save (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
+  unsigned reg1, reg2;
   int sep;
-  int reg1, reg2;
 
   if (!in_prologue ("save"))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    as_bad ("No second operand to .save");
-  sep = parse_operand (&e2);
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    sep = parse_operand (&e2, ',');
+  else
+    e2.X_op = O_absent;
 
   reg1 = e1.X_add_number;
-  reg2 = e2.X_add_number - REG_GR;
-
   /* Make sure its a valid ar.xxx reg, OR its br0, aka 'rp'.  */
-  if (e1.X_op == O_register)
+  if (e1.X_op != O_register)
     {
-      if (e2.X_op == O_register && reg2 >= 0 && reg2 < 128)
-	{
-	  switch (reg1)
-	    {
-	    case REG_AR + AR_BSP:
-	      add_unwind_entry (output_bsp_when ());
-	      add_unwind_entry (output_bsp_gr (reg2));
-	      break;
-	    case REG_AR + AR_BSPSTORE:
-	      add_unwind_entry (output_bspstore_when ());
-	      add_unwind_entry (output_bspstore_gr (reg2));
-	      break;
-	    case REG_AR + AR_RNAT:
-	      add_unwind_entry (output_rnat_when ());
-	      add_unwind_entry (output_rnat_gr (reg2));
-	      break;
-	    case REG_AR + AR_UNAT:
-	      add_unwind_entry (output_unat_when ());
-	      add_unwind_entry (output_unat_gr (reg2));
-	      break;
-	    case REG_AR + AR_FPSR:
-	      add_unwind_entry (output_fpsr_when ());
-	      add_unwind_entry (output_fpsr_gr (reg2));
-	      break;
-	    case REG_AR + AR_PFS:
-	      add_unwind_entry (output_pfs_when ());
-	      if (! (unwind.prologue_mask & 4))
-		add_unwind_entry (output_pfs_gr (reg2));
-	      break;
-	    case REG_AR + AR_LC:
-	      add_unwind_entry (output_lc_when ());
-	      add_unwind_entry (output_lc_gr (reg2));
-	      break;
-	    case REG_BR:
-	      add_unwind_entry (output_rp_when ());
-	      if (! (unwind.prologue_mask & 8))
-		add_unwind_entry (output_rp_gr (reg2));
-	      break;
-	    case REG_PR:
-	      add_unwind_entry (output_preds_when ());
-	      if (! (unwind.prologue_mask & 1))
-		add_unwind_entry (output_preds_gr (reg2));
-	      break;
-	    case REG_PRIUNAT:
-	      add_unwind_entry (output_priunat_when_gr ());
-	      add_unwind_entry (output_priunat_gr (reg2));
-	      break;
-	    default:
-	      as_bad ("First operand not a valid register");
-	    }
-	}
-      else
-	as_bad (" Second operand not a valid register");
+      as_bad ("First operand to .save not a register");
+      reg1 = REG_PR; /* Anything valid is good here.  */
+    }
+  reg2 = e2.X_add_number - REG_GR;
+  if (e2.X_op != O_register || reg2 > 127)
+    {
+      as_bad ("Second operand to .save not a valid register");
+      reg2 = 0;
+    }
+  switch (reg1)
+    {
+    case REG_AR + AR_BSP:
+      add_unwind_entry (output_bsp_when (), sep);
+      add_unwind_entry (output_bsp_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_BSPSTORE:
+      add_unwind_entry (output_bspstore_when (), sep);
+      add_unwind_entry (output_bspstore_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_RNAT:
+      add_unwind_entry (output_rnat_when (), sep);
+      add_unwind_entry (output_rnat_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_UNAT:
+      add_unwind_entry (output_unat_when (), sep);
+      add_unwind_entry (output_unat_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_FPSR:
+      add_unwind_entry (output_fpsr_when (), sep);
+      add_unwind_entry (output_fpsr_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_PFS:
+      add_unwind_entry (output_pfs_when (), sep);
+      if (! (unwind.prologue_mask & 4))
+	add_unwind_entry (output_pfs_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_LC:
+      add_unwind_entry (output_lc_when (), sep);
+      add_unwind_entry (output_lc_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_BR:
+      add_unwind_entry (output_rp_when (), sep);
+      if (! (unwind.prologue_mask & 8))
+	add_unwind_entry (output_rp_gr (reg2), NOT_A_CHAR);
+      break;
+    default:
+      as_bad ("First operand to .save not a valid register");
+    case REG_PR:
+      add_unwind_entry (output_preds_when (), sep);
+      if (! (unwind.prologue_mask & 1))
+	add_unwind_entry (output_preds_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_PRIUNAT:
+      add_unwind_entry (output_priunat_when_gr (), sep);
+      add_unwind_entry (output_priunat_gr (reg2), NOT_A_CHAR);
+      break;
     }
-  else
-    as_bad ("First operand not a register");
 }
 
 static void
 dot_restore (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
+  expressionS e1;
   unsigned long ecount;	/* # of _additional_ regions to pop */
   int sep;
 
   if (!in_body ("restore"))
     return;
 
-  sep = parse_operand (&e1);
+  sep = parse_operand (&e1, ',');
   if (e1.X_op != O_register || e1.X_add_number != REG_GR + 12)
-    {
-      as_bad ("First operand to .restore must be stack pointer (sp)");
-      return;
-    }
+    as_bad ("First operand to .restore must be stack pointer (sp)");
 
   if (sep == ',')
     {
-      parse_operand (&e2);
+      expressionS e2;
+
+      sep = parse_operand (&e2, ',');
       if (e2.X_op != O_constant || e2.X_add_number < 0)
 	{
 	  as_bad ("Second operand to .restore must be a constant >= 0");
-	  return;
+	  e2.X_add_number = 0;
 	}
       ecount = e2.X_add_number;
     }
@@ -3391,10 +3414,10 @@ dot_restore (dummy)
     {
       as_bad ("Epilogue count of %lu exceeds number of nested prologues (%u)",
 	      ecount + 1, unwind.prologue_count);
-      return;
+      ecount = 0;
     }
 
-  add_unwind_entry (output_epilogue (ecount));
+  add_unwind_entry (output_epilogue (ecount), sep);
 
   if (ecount < unwind.prologue_count)
     unwind.prologue_count -= ecount + 1;
@@ -3403,58 +3426,27 @@ dot_restore (dummy)
 }
 
 static void
-dot_restorereg (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  unsigned int ab, reg;
-  expressionS e;
-
-  if (!in_procedure ("restorereg"))
-    return;
-
-  parse_operand (&e);
-
-  if (!convert_expr_to_ab_reg (&e, &ab, &reg))
-    {
-      as_bad ("First operand to .restorereg must be a preserved register");
-      return;
-    }
-  add_unwind_entry (output_spill_reg (ab, reg, 0, 0));
-}
-
-static void
-dot_restorereg_p (dummy)
-     int dummy ATTRIBUTE_UNUSED;
+dot_restorereg (pred)
+     int pred;
 {
   unsigned int qp, ab, reg;
-  expressionS e1, e2;
+  expressionS e;
   int sep;
+  const char * const po = pred ? "restorereg.p" : "restorereg";
 
-  if (!in_procedure ("restorereg.p"))
+  if (!in_procedure (po))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("No second operand to .restorereg.p");
-      return;
-    }
-
-  parse_operand (&e2);
-
-  qp = e1.X_add_number - REG_P;
-  if (e1.X_op != O_register || qp > 63)
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
+  else
     {
-      as_bad ("First operand to .restorereg.p must be a predicate");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
-    {
-      as_bad ("Second operand to .restorereg.p must be a preserved register");
-      return;
-    }
-  add_unwind_entry (output_spill_reg_p (ab, reg, 0, 0, qp));
+  add_unwind_entry (output_spill_reg (ab, reg, 0, 0, qp), sep);
 }
 
 static char *special_linkonce_name[] =
@@ -3582,7 +3574,7 @@ generate_unwind_image (const segT text_s
 
   /* Mark the end of the unwind info, so that we can compute the size of the
      last unwind region.  */
-  add_unwind_entry (output_endp ());
+  add_unwind_entry (output_endp (), NOT_A_CHAR);
 
   /* Force out pending instructions, to make sure all unwind records have
      a valid slot_number field.  */
@@ -3698,12 +3690,14 @@ dot_altrp (dummy)
   if (!in_prologue ("altrp"))
     return;
 
-  parse_operand (&e);
+  parse_operand (&e, 0);
   reg = e.X_add_number - REG_BR;
-  if (e.X_op == O_register && reg < 8)
-    add_unwind_entry (output_rp_br (reg));
-  else
-    as_bad ("First operand not a valid branch register");
+  if (e.X_op != O_register || reg > 7)
+    {
+      as_bad ("First operand to .altrp not a valid branch register");
+      reg = 0;
+    }
+  add_unwind_entry (output_rp_br (reg), 0);
 }
 
 static void
@@ -3713,182 +3707,229 @@ dot_savemem (psprel)
   expressionS e1, e2;
   int sep;
   int reg1, val;
+  const char * const po = psprel ? "savepsp" : "savesp";
 
-  if (!in_prologue (psprel ? "savepsp" : "savesp"))
+  if (!in_prologue (po))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    as_bad ("No second operand to .save%ssp", psprel ? "p" : "");
-  sep = parse_operand (&e2);
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    sep = parse_operand (&e2, ',');
+  else
+    e2.X_op = O_absent;
 
   reg1 = e1.X_add_number;
   val = e2.X_add_number;
 
   /* Make sure its a valid ar.xxx reg, OR its br0, aka 'rp'.  */
-  if (e1.X_op == O_register)
+  if (e1.X_op != O_register)
     {
-      if (e2.X_op == O_constant)
-	{
-	  switch (reg1)
-	    {
-	    case REG_AR + AR_BSP:
-	      add_unwind_entry (output_bsp_when ());
-	      add_unwind_entry ((psprel
-				 ? output_bsp_psprel
-				 : output_bsp_sprel) (val));
-	      break;
-	    case REG_AR + AR_BSPSTORE:
-	      add_unwind_entry (output_bspstore_when ());
-	      add_unwind_entry ((psprel
-				 ? output_bspstore_psprel
-				 : output_bspstore_sprel) (val));
-	      break;
-	    case REG_AR + AR_RNAT:
-	      add_unwind_entry (output_rnat_when ());
-	      add_unwind_entry ((psprel
-				 ? output_rnat_psprel
-				 : output_rnat_sprel) (val));
-	      break;
-	    case REG_AR + AR_UNAT:
-	      add_unwind_entry (output_unat_when ());
-	      add_unwind_entry ((psprel
-				 ? output_unat_psprel
-				 : output_unat_sprel) (val));
-	      break;
-	    case REG_AR + AR_FPSR:
-	      add_unwind_entry (output_fpsr_when ());
-	      add_unwind_entry ((psprel
-				 ? output_fpsr_psprel
-				 : output_fpsr_sprel) (val));
-	      break;
-	    case REG_AR + AR_PFS:
-	      add_unwind_entry (output_pfs_when ());
-	      add_unwind_entry ((psprel
-				 ? output_pfs_psprel
-				 : output_pfs_sprel) (val));
-	      break;
-	    case REG_AR + AR_LC:
-	      add_unwind_entry (output_lc_when ());
-	      add_unwind_entry ((psprel
-				 ? output_lc_psprel
-				 : output_lc_sprel) (val));
-	      break;
-	    case REG_BR:
-	      add_unwind_entry (output_rp_when ());
-	      add_unwind_entry ((psprel
-				 ? output_rp_psprel
-				 : output_rp_sprel) (val));
-	      break;
-	    case REG_PR:
-	      add_unwind_entry (output_preds_when ());
-	      add_unwind_entry ((psprel
-				 ? output_preds_psprel
-				 : output_preds_sprel) (val));
-	      break;
-	    case REG_PRIUNAT:
-	      add_unwind_entry (output_priunat_when_mem ());
-	      add_unwind_entry ((psprel
-				 ? output_priunat_psprel
-				 : output_priunat_sprel) (val));
-	      break;
-	    default:
-	      as_bad ("First operand not a valid register");
-	    }
-	}
-      else
-	as_bad (" Second operand not a valid constant");
+      as_bad ("First operand to .%s not a register", po);
+      reg1 = REG_PR; /* Anything valid is good here.  */
+    }
+  if (e2.X_op != O_constant)
+    {
+      as_bad ("Second operand to .%s not a constant", po);
+      val = 0;
+    }
+
+  switch (reg1)
+    {
+    case REG_AR + AR_BSP:
+      add_unwind_entry (output_bsp_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_bsp_psprel
+			 : output_bsp_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_BSPSTORE:
+      add_unwind_entry (output_bspstore_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_bspstore_psprel
+			 : output_bspstore_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_RNAT:
+      add_unwind_entry (output_rnat_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_rnat_psprel
+			 : output_rnat_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_UNAT:
+      add_unwind_entry (output_unat_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_unat_psprel
+			 : output_unat_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_FPSR:
+      add_unwind_entry (output_fpsr_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_fpsr_psprel
+			 : output_fpsr_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_PFS:
+      add_unwind_entry (output_pfs_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_pfs_psprel
+			 : output_pfs_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_LC:
+      add_unwind_entry (output_lc_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_lc_psprel
+			 : output_lc_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_BR:
+      add_unwind_entry (output_rp_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_rp_psprel
+			 : output_rp_sprel) (val), NOT_A_CHAR);
+      break;
+    default:
+      as_bad ("First operand to .%s not a valid register", po);
+    case REG_PR:
+      add_unwind_entry (output_preds_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_preds_psprel
+			 : output_preds_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_PRIUNAT:
+      add_unwind_entry (output_priunat_when_mem (), sep);
+      add_unwind_entry ((psprel
+			 ? output_priunat_psprel
+			 : output_priunat_sprel) (val), NOT_A_CHAR);
+      break;
     }
-  else
-    as_bad ("First operand not a register");
 }
 
+/* Perhaps this has a better place...  */
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# define popcount __builtin_popcount
+#else
+static int
+popcount (unsigned x)
+{
+  static const unsigned char popcnt[16] =
+    {
+      0, 1, 1, 2,
+      1, 2, 2, 3,
+      1, 2, 2, 3,
+      2, 3, 3, 4
+    };
+
+  if (x < NELEMS (popcnt))
+    return popcnt[x];
+  return popcnt[x % NELEMS (popcnt)] + popcount (x / NELEMS (popcnt));
+}
+#endif
+
 static void
 dot_saveg (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
+  expressionS e;
+  unsigned grmask;
   int sep;
 
   if (!in_prologue ("save.g"))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep == ',')
-    parse_operand (&e2);
+  sep = parse_operand (&e, ',');
 
-  if (e1.X_op != O_constant)
-    as_bad ("First operand to .save.g must be a constant.");
-  else
+  grmask = e.X_add_number;
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0xf)
     {
-      int grmask = e1.X_add_number;
-      if (sep != ',')
-	add_unwind_entry (output_gr_mem (grmask));
-      else
+      as_bad ("First operand to .save.g must be a positive 4-bit constant");
+      grmask = 0;
+    }
+
+  if (sep == ',')
+    {
+      unsigned reg;
+      int n = popcount (grmask);
+
+      parse_operand (&e, 0);
+      reg = e.X_add_number - REG_GR;
+      if (e.X_op != O_register || reg > 127)
 	{
-	  int reg = e2.X_add_number - REG_GR;
-	  if (e2.X_op == O_register && reg >= 0 && reg < 128)
-	    add_unwind_entry (output_gr_gr (grmask, reg));
-	  else
-	    as_bad ("Second operand is an invalid register.");
+	  as_bad ("Second operand to .save.g must be a general register");
+	  reg = 0;
 	}
+      else if (reg > 128U - n)
+	{
+	  as_bad ("Second operand to .save.g must be the first of %d general registers", n);
+	  reg = 0;
+	}
+      add_unwind_entry (output_gr_gr (grmask, reg), 0);
     }
+  else
+    add_unwind_entry (output_gr_mem (grmask), 0);
 }
 
 static void
 dot_savef (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1;
-  int sep;
+  expressionS e;
 
   if (!in_prologue ("save.f"))
     return;
 
-  sep = parse_operand (&e1);
+  parse_operand (&e, 0);
 
-  if (e1.X_op != O_constant)
-    as_bad ("Operand to .save.f must be a constant.");
-  else
-    add_unwind_entry (output_fr_mem (e1.X_add_number));
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0xfffff)
+    {
+      as_bad ("Operand to .save.f must be a positive 20-bit constant");
+      e.X_add_number = 0;
+    }
+  add_unwind_entry (output_fr_mem (e.X_add_number), 0);
 }
 
 static void
 dot_saveb (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
-  unsigned int reg;
-  unsigned char sep;
-  int brmask;
+  expressionS e;
+  unsigned brmask;
+  int sep;
 
   if (!in_prologue ("save.b"))
     return;
 
-  sep = parse_operand (&e1);
-  if (e1.X_op != O_constant)
+  sep = parse_operand (&e, ',');
+
+  brmask = e.X_add_number;
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0x1f)
     {
-      as_bad ("First operand to .save.b must be a constant.");
-      return;
+      as_bad ("First operand to .save.b must be a positive 5-bit constant");
+      brmask = 0;
     }
-  brmask = e1.X_add_number;
 
   if (sep == ',')
     {
-      sep = parse_operand (&e2);
-      reg = e2.X_add_number - REG_GR;
-      if (e2.X_op != O_register || reg > 127)
+      unsigned reg;
+      int n = popcount (brmask);
+
+      parse_operand (&e, 0);
+      reg = e.X_add_number - REG_GR;
+      if (e.X_op != O_register || reg > 127)
 	{
-	  as_bad ("Second operand to .save.b must be a general register.");
-	  return;
+	  as_bad ("Second operand to .save.b must be a general register");
+	  reg = 0;
 	}
-      add_unwind_entry (output_br_gr (brmask, e2.X_add_number));
+      else if (reg > 128U - n)
+	{
+	  as_bad ("Second operand to .save.b must be the first of %d general registers", n);
+	  reg = 0;
+	}
+      add_unwind_entry (output_br_gr (brmask, reg), 0);
     }
   else
-    add_unwind_entry (output_br_mem (brmask));
-
-  if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    demand_empty_rest_of_line ();
+    add_unwind_entry (output_br_mem (brmask), 0);
 }
 
 static void
@@ -3896,23 +3937,38 @@ dot_savegf (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
-  int sep;
 
   if (!in_prologue ("save.gf"))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep == ',')
-    parse_operand (&e2);
-
-  if (e1.X_op != O_constant || sep != ',' || e2.X_op != O_constant)
-    as_bad ("Both operands of .save.gf must be constants.");
+  if (parse_operand (&e1, ',') == ',')
+    parse_operand (&e2, 0);
   else
-    {
-      int grmask = e1.X_add_number;
-      int frmask = e2.X_add_number;
-      add_unwind_entry (output_frgr_mem (grmask, frmask));
-    }
+    e2.X_op = O_absent;
+
+  if (e1.X_op != O_constant
+      || e1.X_add_number < 0
+      || e1.X_add_number > 0xf)
+    {
+      as_bad ("First operand to .save.gf must be a non-negative 4-bit constant");
+      e1.X_op = O_absent;
+      e1.X_add_number = 0;
+    }
+  if (e2.X_op != O_constant
+      || e2.X_add_number < 0
+      || e2.X_add_number > 0xfffff)
+    {
+      as_bad ("Second operand to .save.gf must be a non-negative 20-bit constant");
+      e2.X_op = O_absent;
+      e2.X_add_number = 0;
+    }
+  if (e1.X_op == O_constant
+      && e2.X_op == O_constant
+      && e1.X_add_number == 0
+      && e2.X_add_number == 0)
+    as_bad ("Operands to .save.gf may not be both zero");
+
+  add_unwind_entry (output_frgr_mem (e1.X_add_number, e2.X_add_number), 0);
 }
 
 static void
@@ -3920,201 +3976,90 @@ dot_spill (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   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 ())
-    demand_empty_rest_of_line ();
+  parse_operand (&e, 0);
 
   if (e.X_op != O_constant)
-    as_bad ("Operand to .spill must be a constant");
-  else
-    add_unwind_entry (output_spill_base (e.X_add_number));
-}
-
-static void
-dot_spillreg (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  int sep;
-  unsigned int ab, xy, reg, treg;
-  expressionS e1, e2;
-
-  if (!in_procedure ("spillreg"))
-    return;
-
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("No second operand to .spillreg");
-      return;
-    }
-
-  parse_operand (&e2);
-
-  if (!convert_expr_to_ab_reg (&e1, &ab, &reg))
-    {
-      as_bad ("First operand to .spillreg must be a preserved register");
-      return;
-    }
-
-  if (!convert_expr_to_xy_reg (&e2, &xy, &treg))
     {
-      as_bad ("Second operand to .spillreg must be a register");
-      return;
+      as_bad ("Operand to .spill must be a constant");
+      e.X_add_number = 0;
     }
-
-  add_unwind_entry (output_spill_reg (ab, reg, treg, xy));
+  add_unwind_entry (output_spill_base (e.X_add_number), 0);
 }
 
 static void
-dot_spillmem (psprel)
-     int psprel;
+dot_spillreg (pred)
+     int pred;
 {
-  expressionS e1, e2;
   int sep;
-  unsigned int ab, reg;
+  unsigned int qp, ab, xy, reg, treg;
+  expressionS e;
+  const char * const po = pred ? "spillreg.p" : "spillreg";
 
-  if (!in_procedure ("spillmem"))
+  if (!in_procedure (po))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand missing");
-      return;
-    }
-
-  parse_operand (&e2);
-
-  if (!convert_expr_to_ab_reg (&e1, &ab, &reg))
-    {
-      as_bad ("First operand to .spill%s must be a preserved register",
-	      psprel ? "psp" : "sp");
-      return;
-    }
-
-  if (e2.X_op != O_constant)
-    {
-      as_bad ("Second operand to .spill%s must be a constant",
-	      psprel ? "psp" : "sp");
-      return;
-    }
-
-  if (psprel)
-    add_unwind_entry (output_spill_psprel (ab, reg, e2.X_add_number));
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
   else
-    add_unwind_entry (output_spill_sprel (ab, reg, e2.X_add_number));
-}
-
-static void
-dot_spillreg_p (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  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 != ',')
-    {
-      as_bad ("No second and third operand to .spillreg.p");
-      return;
-    }
-
-  sep = parse_operand (&e2);
-  if (sep != ',')
     {
-      as_bad ("No third operand to .spillreg.p");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  parse_operand (&e3);
-
-  qp = e1.X_add_number - REG_P;
-
-  if (e1.X_op != O_register || qp > 63)
-    {
-      as_bad ("First operand to .spillreg.p must be a predicate");
-      return;
-    }
-
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
-    {
-      as_bad ("Second operand to .spillreg.p must be a preserved register");
-      return;
-    }
-
-  if (!convert_expr_to_xy_reg (&e3, &xy, &treg))
-    {
-      as_bad ("Third operand to .spillreg.p must be a register");
-      return;
-    }
+  if (sep == ',')
+    sep = parse_operand (&e, ',');
+  else
+    e.X_op = O_absent;
+  convert_expr_to_xy_reg (&e, &xy, &treg, po, 2 + pred);
 
-  add_unwind_entry (output_spill_reg_p (ab, reg, treg, xy, qp));
+  add_unwind_entry (output_spill_reg (ab, reg, treg, xy, qp), sep);
 }
 
 static void
-dot_spillmem_p (psprel)
+dot_spillmem (psprel)
      int psprel;
 {
-  expressionS e1, e2, e3;
-  int sep;
-  unsigned int ab, reg;
-  unsigned int qp;
-
-  if (!in_procedure ("spillmem.p"))
-    return;
-
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand missing");
-      return;
-    }
-
-  parse_operand (&e2);
-  if (sep != ',')
-    {
-      as_bad ("Second operand missing");
-      return;
-    }
+  expressionS e;
+  int pred = (psprel < 0), sep;
+  unsigned int qp, ab, reg;
+  const char * po;
 
-  parse_operand (&e3);
+  if (pred)
+    po = (psprel = ~psprel) ? "spillpsp.p" : "spillsp.p";
+  else
+    po = psprel ? "spillpsp" : "spillsp";
 
-  qp = e1.X_add_number - REG_P;
-  if (e1.X_op != O_register || qp > 63)
-    {
-      as_bad ("First operand to .spill%s_p must be a predicate",
-	      psprel ? "psp" : "sp");
-      return;
-    }
+  if (!in_procedure (po))
+    return;
 
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
+  else
     {
-      as_bad ("Second operand to .spill%s_p must be a preserved register",
-	      psprel ? "psp" : "sp");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  if (e3.X_op != O_constant)
+  if (sep == ',')
+    sep = parse_operand (&e, ',');
+  else
+    e.X_op = O_absent;
+  if (e.X_op != O_constant)
     {
-      as_bad ("Third operand to .spill%s_p must be a constant",
-	      psprel ? "psp" : "sp");
-      return;
+      as_bad ("Operand %d to .%s must be a constant", 2 + pred, po);
+      e.X_add_number = 0;
     }
 
   if (psprel)
-    add_unwind_entry (output_spill_psprel_p (ab, reg, e3.X_add_number, qp));
+    add_unwind_entry (output_spill_psprel (ab, reg, e.X_add_number, qp), sep);
   else
-    add_unwind_entry (output_spill_sprel_p (ab, reg, e3.X_add_number, qp));
+    add_unwind_entry (output_spill_sprel (ab, reg, e.X_add_number, qp), sep);
 }
 
 static unsigned int
@@ -4181,14 +4126,15 @@ dot_label_state (dummy)
   if (!in_body ("label_state"))
     return;
 
-  parse_operand (&e);
-  if (e.X_op != O_constant)
+  parse_operand (&e, 0);
+  if (e.X_op == O_constant)
+    save_prologue_count (e.X_add_number, unwind.prologue_count);
+  else
     {
       as_bad ("Operand to .label_state must be a constant");
-      return;
+      e.X_add_number = 0;
     }
-  add_unwind_entry (output_label_state (e.X_add_number));
-  save_prologue_count (e.X_add_number, unwind.prologue_count);
+  add_unwind_entry (output_label_state (e.X_add_number), 0);
 }
 
 static void
@@ -4200,14 +4146,15 @@ dot_copy_state (dummy)
   if (!in_body ("copy_state"))
     return;
 
-  parse_operand (&e);
-  if (e.X_op != O_constant)
+  parse_operand (&e, 0);
+  if (e.X_op == O_constant)
+    unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
+  else
     {
       as_bad ("Operand to .copy_state must be a constant");
-      return;
+      e.X_add_number = 0;
     }
-  add_unwind_entry (output_copy_state (e.X_add_number));
-  unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
+  add_unwind_entry (output_copy_state (e.X_add_number), 0);
 }
 
 static void
@@ -4217,32 +4164,28 @@ dot_unwabi (dummy)
   expressionS e1, e2;
   unsigned char sep;
 
-  if (!in_procedure ("unwabi"))
+  if (!in_prologue ("unwabi"))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand to .unwabi missing");
-      return;
-    }
-  sep = parse_operand (&e2);
-  if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    demand_empty_rest_of_line ();
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    parse_operand (&e2, 0);
+  else
+    e2.X_op = O_absent;
 
   if (e1.X_op != O_constant)
     {
       as_bad ("First operand to .unwabi must be a constant");
-      return;
+      e1.X_add_number = 0;
     }
 
   if (e2.X_op != O_constant)
     {
       as_bad ("Second operand to .unwabi must be a constant");
-      return;
+      e2.X_add_number = 0;
     }
 
-  add_unwind_entry (output_unwabi (e1.X_add_number, e2.X_add_number));
+  add_unwind_entry (output_unwabi (e1.X_add_number, e2.X_add_number), 0);
 }
 
 static void
@@ -4324,16 +4267,14 @@ dot_body (dummy)
   unwind.prologue_mask = 0;
   unwind.body = 1;
 
-  add_unwind_entry (output_body ());
-  demand_empty_rest_of_line ();
+  add_unwind_entry (output_body (), 0);
 }
 
 static void
 dot_prologue (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  unsigned char sep;
-  int mask = 0, grsave = 0;
+  unsigned mask = 0, grsave = 0;
 
   if (!in_procedure ("prologue"))
     return;
@@ -4348,33 +4289,49 @@ dot_prologue (dummy)
 
   if (!is_it_end_of_statement ())
     {
-      expressionS e1, e2;
-      sep = parse_operand (&e1);
-      if (sep != ',')
-	as_bad ("No second operand to .prologue");
-      sep = parse_operand (&e2);
-      if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-	demand_empty_rest_of_line ();
-
-      if (e1.X_op == O_constant)
-	{
-	  mask = e1.X_add_number;
+      expressionS e;
+      int n, sep = parse_operand (&e, ',');
 
-	  if (e2.X_op == O_constant)
-	    grsave = e2.X_add_number;
-	  else if (e2.X_op == O_register
-		   && (grsave = e2.X_add_number - REG_GR) < 128)
-	    ;
-	  else
-	    as_bad ("Second operand not a constant or general register");
+      if (e.X_op != O_constant
+	  || e.X_add_number < 0
+	  || e.X_add_number > 0xf)
+	as_bad ("First operand to .prologue must be a positive 4-bit constant");
+      else if (e.X_add_number == 0)
+	as_warn ("Pointless use of zero first operand to .prologue");
+      else
+	mask = e.X_add_number;
+	n = popcount (mask);
 
-	  add_unwind_entry (output_prologue_gr (mask, grsave));
-	}
+      if (sep == ',')
+	parse_operand (&e, 0);
       else
-	as_bad ("First operand not a constant");
+	e.X_op = O_absent;
+      if (e.X_op == O_constant
+	  && e.X_add_number >= 0
+	  && e.X_add_number < 128)
+	{
+	  if (md.unwind_check == unwind_check_error)
+	    as_warn ("Using a constant as second operand to .prologue is deprecated");
+	  grsave = e.X_add_number;
+	}
+      else if (e.X_op != O_register
+	       || (grsave = e.X_add_number - REG_GR) > 127)
+	{
+	  as_bad ("Second operand to .prologue must be a general register");
+	  grsave = 0;
+	}
+      else if (grsave > 128U - n)
+	{
+	  as_bad ("Second operand to .prologue must be the first of %d general registers", n);
+	  grsave = 0;
+	}
+
     }
+
+  if (mask)
+    add_unwind_entry (output_prologue_gr (mask, grsave), 0);
   else
-    add_unwind_entry (output_prologue ());
+    add_unwind_entry (output_prologue (), 0);
 
   unwind.prologue = 1;
   unwind.prologue_mask = mask;
@@ -5282,7 +5234,7 @@ const pseudo_typeS md_pseudo_table[] =
     { "save", dot_save, 0 },
     { "restore", dot_restore, 0 },
     { "restorereg", dot_restorereg, 0 },
-    { "restorereg.p", dot_restorereg_p, 0 },
+    { "restorereg.p", dot_restorereg, 1 },
     { "handlerdata", dot_handlerdata, 0 },
     { "unwentry", dot_unwentry, 0 },
     { "altrp", dot_altrp, 0 },
@@ -5296,9 +5248,9 @@ const pseudo_typeS md_pseudo_table[] =
     { "spillreg", dot_spillreg, 0 },
     { "spillsp", dot_spillmem, 0 },
     { "spillpsp", dot_spillmem, 1 },
-    { "spillreg.p", dot_spillreg_p, 0 },
-    { "spillsp.p", dot_spillmem_p, 0 },
-    { "spillpsp.p", dot_spillmem_p, 1 },
+    { "spillreg.p", dot_spillreg, 1 },
+    { "spillsp.p", dot_spillmem, ~0 },
+    { "spillpsp.p", dot_spillmem, ~1 },
     { "label_state", dot_label_state, 0 },
     { "copy_state", dot_copy_state, 0 },
     { "unwabi", dot_unwabi, 0 },
@@ -5991,27 +5943,19 @@ operand_match (idesc, index, e)
 }
 
 static int
-parse_operand (e)
+parse_operand (e, more)
      expressionS *e;
+     int more;
 {
   int sep = '\0';
 
   memset (e, 0, sizeof (*e));
   e->X_op = O_absent;
   SKIP_WHITESPACE ();
-  if (*input_line_pointer != '}')
-    expression (e);
-  sep = *input_line_pointer++;
-
-  if (sep == '}')
-    {
-      if (!md.manual_bundling)
-	as_warn ("Found '}' when manual bundling is off");
-      else
-	CURR_SLOT.manual_bundling_off = 1;
-      md.manual_bundling = 0;
-      sep = '\0';
-    }
+  expression (e);
+  sep = *input_line_pointer;
+  if (more && (sep == ',' || sep == more))
+    ++input_line_pointer;
   return sep;
 }
 
@@ -6070,7 +6014,7 @@ parse_operands (idesc)
     {
       if (i < NELEMS (CURR_SLOT.opnd)) 
 	{
-	  sep = parse_operand (CURR_SLOT.opnd + i);
+	  sep = parse_operand (CURR_SLOT.opnd + i, '=');
 	  if (CURR_SLOT.opnd[i].X_op == O_absent)
 	    break;
 	}
@@ -6078,7 +6022,7 @@ parse_operands (idesc)
 	{
 	  expressionS dummy;
 
-	  sep = parse_operand (&dummy);
+	  sep = parse_operand (&dummy, '=');
 	  if (dummy.X_op == O_absent)
 	    break;
 	}
@@ -6123,7 +6067,7 @@ parse_operands (idesc)
 	  /* now we can parse the first arg:  */
 	  saved_input_pointer = input_line_pointer;
 	  input_line_pointer = first_arg;
-	  sep = parse_operand (CURR_SLOT.opnd + 0);
+	  sep = parse_operand (CURR_SLOT.opnd + 0, '=');
 	  if (sep != '=')
 	    --num_outputs;	/* force error */
 	  input_line_pointer = saved_input_pointer;
@@ -7548,10 +7487,17 @@ ia64_end_of_source ()
 void
 ia64_start_line ()
 {
+  static int first;
+
   if (md.qp.X_op == O_register)
     as_bad ("qualifying predicate not followed by instruction");
   md.qp.X_op = O_absent;
 
+  if (!first) {
+    first = 1;
+    return;
+  }
+
   if (ignore_input ())
     return;
 
@@ -7570,6 +7516,21 @@ ia64_start_line ()
       else
 	insn_group_break (1, 0, 0);
     }
+  else if (input_line_pointer[-1] == '}')
+    {
+      if (!md.manual_bundling)
+	as_warn ("Found '}' when manual bundling is off");
+      else
+	PREV_SLOT.manual_bundling_off = 1;
+      md.manual_bundling = 0;
+
+      /* switch back to automatic mode, if applicable */
+      if (md.detect_dv
+	  && md.explicit_mode
+	  && !md.mode_explicitly_set
+	  && !md.default_explicit_mode)
+	dot_dv_mode ('A');
+    }
 }
 
 /* This is a hook for ia64_frob_label, so that it can distinguish tags from
@@ -7620,32 +7581,6 @@ ia64_unrecognized_line (ch)
 	}
       return 1;
 
-    case '}':
-      if (!md.manual_bundling)
-	as_warn ("Found '}' when manual bundling is off");
-      else
-	PREV_SLOT.manual_bundling_off = 1;
-      md.manual_bundling = 0;
-
-      /* switch back to automatic mode, if applicable */
-      if (md.detect_dv
-	  && md.explicit_mode
-	  && !md.mode_explicitly_set
-	  && !md.default_explicit_mode)
-	dot_dv_mode ('A');
-
-      /* Allow '{' to follow on the same line.  We also allow ";;", but that
-	 happens automatically because ';' is an end of line marker.  */
-      SKIP_WHITESPACE ();
-      if (input_line_pointer[0] == '{')
-	{
-	  input_line_pointer++;
-	  return ia64_unrecognized_line ('{');
-	}
-
-      demand_empty_rest_of_line ();
-      return 1;
-
     case '[':
       {
 	char *s;
@@ -11665,7 +11601,7 @@ dot_alias (int section)
   if (name == end_name)
     {
       as_bad (_("expected symbol name"));
-      discard_rest_of_line ();
+      ignore_rest_of_line ();
       return;
     }
 
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/config/tc-ia64.h	2005-05-06 08:24:28.000000000 +0200
+++ 2005-05-18/gas/config/tc-ia64.h	2005-05-20 16:41:30.000000000 +0200
@@ -213,9 +213,9 @@ extern void ia64_convert_frag (fragS *);
 
 typedef enum
 {
-  prologue, prologue_gr, body, mem_stack_f, mem_stack_v, psp_gr, psp_sprel,
-  rp_when, rp_gr, rp_br, rp_psprel, rp_sprel, pfs_when, pfs_gr, pfs_psprel,
-  pfs_sprel, preds_when, preds_gr, preds_psprel, preds_sprel,
+  prologue, prologue_gr, body, mem_stack_f, mem_stack_v, psp_gr, psp_psprel,
+  psp_sprel, rp_when, rp_gr, rp_br, rp_psprel, rp_sprel, pfs_when, pfs_gr,
+  pfs_psprel, pfs_sprel, preds_when, preds_gr, preds_psprel, preds_sprel,
   fr_mem, frgr_mem, gr_gr, gr_mem, br_mem, br_gr, spill_base, spill_mask,
   unat_when, unat_gr, unat_psprel, unat_sprel, lc_when, lc_gr, lc_psprel,
   lc_sprel, fpsr_when, fpsr_gr, fpsr_psprel, fpsr_sprel,
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/group-2.s	2005-05-08 00:19:37.000000000 +0200
+++ 2005-05-18/gas/testsuite/gas/ia64/group-2.s	2005-05-20 16:53:54.000000000 +0200
@@ -1,6 +1,6 @@
 	.section	.gnu.linkonce.t.foo,"axG",@progbits,foo,comdat
 	.proc foo#
 foo:
-	.prologue 12, 33
+	.prologue 12, r33
 	;;
 	.endp foo#
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/ia64.exp	2005-05-09 08:31:39.000000000 +0200
+++ 2005-05-18/gas/testsuite/gas/ia64/ia64.exp	2005-05-23 11:46:47.236009550 +0200
@@ -81,7 +81,10 @@ if [istarget "ia64-*"] then {
     run_list_test "pound" "-al"
     run_list_test "proc" "-munwind-check=error"
     run_list_test "slot2" ""
+    run_dump_test "strange"
+    run_list_test "unwind-bad" ""
     run_list_test "unwind-err" "-munwind-check=error"
+    run_dump_test "unwind-ok"
     run_dump_test "operand-or"
     run_list_test "hint.b-err" ""
     run_list_test "hint.b-warn" "-mhint.b=warning"
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/strange.d	1970-01-01 01:00:00.000000000 +0100
+++ 2005-05-18/gas/testsuite/gas/ia64/strange.d	2005-05-19 16:37:51.000000000 +0200
@@ -0,0 +1,13 @@
+#objdump: -s
+#name: ia64 strange
+
+.*: +file format .*
+
+Contents of section .text:
+ 0000 0c000000 01001000 00020000 00000400  .*
+ 0010 04000000 01000000 00000020 00000400  .*
+ 0020 0c000000 01002000 00020000 00000400  .*
+ 0030 04000000 01000000 00000040 00000400  .*
+ 0040 1d000000 01003000 00020080 00008400  .*
+Contents of section .data:
+ 0000 ffff                                 .*              
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/strange.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-05-18/gas/testsuite/gas/ia64/strange.s	2005-05-19 16:35:55.000000000 +0200
@@ -0,0 +1,12 @@
+.explicit
+.text
+_start:
+{.mfi
+	nop.f		1 } nop.x 1
+{.mfi
+	nop.f		2
+}	nop.x		2
+{.mfb
+	nop.f		3
+	br.ret.sptk	rp
+.xdata1 .data, -1 } .xdata1 .data, -1
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/unwind-bad.l	1970-01-01 01:00:00.000000000 +0100
+++ 2005-05-18/gas/testsuite/gas/ia64/unwind-bad.l	2005-05-20 16:34:10.000000000 +0200
@@ -0,0 +1,51 @@
+.*: Assembler messages:
+.*:8: Error: First operand to \.save\.g must be a positive 4-bit constant
+.*:10: Error: First operand to \.save\.g must be a positive 4-bit constant
+.*:12: Error: First operand to \.save\.g must be a positive 4-bit constant
+#FIXME .*:16: Error: Previous spill incomplete
+#FIXME .*:18: Error: Register r4 was already saved
+.*:20: Error: Operand to \.save\.f must be a positive 20-bit constant
+.*:22: Error: Operand to \.save\.f must be a positive 20-bit constant
+.*:24: Error: Operand to \.save\.f must be a positive 20-bit constant
+#FIXME .*:28: Error: Previous spill incomplete
+#FIXME .*:30: Error: Register f2 was already saved
+.*:32: Error: First operand to \.save\.b must be a positive 5-bit constant
+.*:34: Error: First operand to \.save\.b must be a positive 5-bit constant
+.*:36: Error: First operand to \.save\.b must be a positive 5-bit constant
+#FIXME .*:40: Error: Previous spill incomplete
+#FIXME .*:42: Error: Register b1 was already saved
+.*:44: Error: Operand 2 to \.spillreg must be a writable register
+.*:46: Error: Operand 1 to \.spillreg must be a preserved register
+.*:48: Error: Operand 1 to \.spillreg must be a preserved register
+.*:50: Error: Operand 1 to \.spillreg must be a preserved register
+.*:52: Error: Operand 2 to \.spillreg must be a writable register
+.*:54: Error: Operand 2 to \.spillreg must be a writable register
+.*:56: Error: Operand 1 to \.spillreg must be a preserved register
+#FIXME .*:58: Error: Floating point register cannot be spilled to general register
+#FIXME .*:60: Error: Floating point register cannot be spilled to branch register
+.*:62: Warning: Pointless use of p0 as first operand to \.spillreg\.p
+.*:64: Error: Operand 3 to \.spillreg.p must be a writable register
+.*:66: Error: Operand 3 to \.spillreg.p must be a writable register
+.*:68: Warning: Pointless use of p0 as first operand to \.restorereg\.p
+.*:78: Error: Operands to \.save\.gf may not be both zero
+.*:80: Error: First operand to \.save\.gf must be a non-negative 4-bit constant
+.*:82: Error: Second operand to \.save\.gf must be a non-negative 20-bit constant
+.*:84: Error: First operand to \.save\.gf must be a non-negative 4-bit constant
+.*:86: Error: Second operand to \.save\.gf must be a non-negative 20-bit constant
+#FIXME .*:90: Error: Previous spill incomplete
+#FIXME .*:92: Error: Register r4 was already saved
+#FIXME .*:94: Error: Register f2 was already saved
+.*:98: Error: Epilogue count of 2 exceeds number of nested prologues \(1\)
+.*:100: Error: Missing \.label_state 2
+.*:108: Error: First operand to \.save\.g must be a positive 4-bit constant
+#FIXME .*:110: Error: Second operand to \.save\.g must be a writable general registers
+.*:112: Error: Second operand to \.save\.g must be the first of 2 general registers
+.*:115: Error: First operand to \.save\.b must be a positive 5-bit constant
+#FIXME .*:117: Error: Second operand to \.save\.b must be a writable general registers
+.*:119: Error: Second operand to \.save\.b must be the first of 2 general registers
+.*:128: Error: First operand to \.prologue must be a positive 4-bit constant
+.*:134: Warning: Pointless use of zero first operand to \.prologue
+.*:140: Error: First operand to \.prologue must be a positive 4-bit constant
+#FIXME .*:141: Error: Operand to \.vframe must be a writable general registers
+#FIXME .*:147: Error: Second operand to \.prologue must be a writable general registers
+.*:153: Error: Second operand to \.prologue must be the first of 2 general registers
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/unwind-bad.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-05-18/gas/testsuite/gas/ia64/unwind-bad.s	2005-05-20 16:23:42.000000000 +0200
@@ -0,0 +1,155 @@
+.text
+
+.proc full1
+full1:
+
+.prologue
+.spill 0
+.save.g 0
+	nop		0
+.save.g 0x10
+	nop		0
+.save.g -1
+	nop		0
+.save.g 0x3
+	nop		0
+.save.g 0x4
+	nop		0
+.save.g 0x1
+	nop		0
+.save.f 0
+	nop		0
+.save.f 0x100000
+	nop		0
+.save.f -1
+	nop		0
+.save.f 0x3
+	nop		0
+.save.f 0x4
+	nop		0
+.save.f 0x1
+	nop		0
+.save.b 0
+	nop		0
+.save.b 0x20
+	nop		0
+.save.b -1
+	nop		0
+.save.b 0x3
+	nop		0
+.save.b 0x4
+	nop		0
+.save.b 0x1
+	nop		0
+.spillreg r4, r0
+	nop		0
+.spillreg r3, r2
+	nop		0
+.spillreg r8, r9
+	nop		0
+.spillreg b6, r10
+	nop		0
+.spillreg f2, f0
+	nop		0
+.spillreg f3, f1
+	nop		0
+.spillreg f6, f7
+	nop		0
+.spillreg f4, r11
+	nop		0
+.spillreg f5, b0
+	nop		0
+.spillreg.p p0, r4, r3
+	nop		0
+.spillreg.p p1, r4, r0
+	nop		0
+.spillreg.p p1, f16, f0
+	nop		0
+.restorereg.p p0, r4
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp full1
+
+.proc full2
+full2:
+.prologue
+.spill 0
+.save.gf 0, 0
+	nop		0
+.save.gf 0x10, 0
+	nop		0
+.save.gf 0, 0x100000
+	nop		0
+.save.gf ~0, 0
+	nop		0
+.save.gf 0, ~0
+	nop		0
+.save.gf 1, 1
+	nop		0
+.save.gf 2, 0
+	nop		0
+.save.gf 1, 0
+	nop		0
+.save.gf 0, 1
+	nop		0
+.body
+.label_state 1
+.restore sp, 1
+	nop.x		0
+.copy_state 2
+	br.ret.sptk	rp
+.endp full2
+
+.proc full3
+full3:
+.prologue
+.spill 0
+.save.g 0x10, r16
+	nop		0
+.save.g 0x01, r0
+	nop		0
+.save.g 0x06, r127
+	nop		0
+	nop		0
+.save.b 0x20, r16
+	nop		0
+.save.b 0x01, r0
+	nop		0
+.save.b 0x18, r127
+	nop		0
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp full3
+
+.proc simple1
+simple1:
+.prologue 0x10, r2
+	br.ret.sptk	rp
+.endp simple1
+
+.proc simple2
+simple2:
+.prologue 0, r2
+	br.ret.sptk	rp
+.endp simple2
+
+.proc simple3
+simple3:
+.prologue -1, r2
+.vframe r0
+	br.ret.sptk	rp
+.endp simple3
+
+.proc simple4
+simple4:
+.prologue 0x1, r0
+	br.ret.sptk	rp
+.endp simple4
+
+.proc simple5
+simple5:
+.prologue 0xc, r127
+	br.ret.sptk	rp
+.endp simple5
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/unwind-err.l	2005-02-08 10:38:38.000000000 +0100
+++ 2005-05-18/gas/testsuite/gas/ia64/unwind-err.l	2005-05-20 16:41:30.000000000 +0200
@@ -8,27 +8,29 @@
 .*:7: Error: .body outside of procedure
 .*:8: Error: .spillreg outside of procedure
 .*:9: Error: .spillreg.p outside of procedure
-.*:10: Error: .spillmem outside of procedure
-.*:11: Error: .spillmem.p outside of procedure
-.*:12: Error: .spillmem outside of procedure
-.*:13: Error: .spillmem.p outside of procedure
+.*:10: Error: .spillsp outside of procedure
+.*:11: Error: .spillsp.p outside of procedure
+.*:12: Error: .spillpsp outside of procedure
+.*:13: Error: .spillpsp.p outside of procedure
 .*:14: Error: .restorereg outside of procedure
 .*:15: Error: .restorereg.p outside of procedure
 .*:24: Error: .label_state outside of body region
 .*:25: Error: .copy_state outside of body region
 .*:26: Error: .fframe outside of prologue
 .*:27: Error: .vframe outside of prologue
-.*:28: Error: .spill outside of prologue
-.*:29: Error: .restore outside of body region
-.*:30: Error: .save outside of prologue
-.*:31: Error: .savesp outside of prologue
-.*:32: Error: .savepsp outside of prologue
-.*:33: Error: .save.g outside of prologue
-.*:34: Error: .save.gf outside of prologue
-.*:35: Error: .save.f outside of prologue
-.*:36: Error: .save.b outside of prologue
-.*:37: Error: .altrp outside of prologue
-.*:42: Error: .prologue within prologue
-.*:50: Error: .body outside of procedure
-.*:57: Warning: Initial .prologue.*
-.*:64: Warning: Initial .body.*
+.*:28: Error: .vframesp outside of prologue
+.*:29: Error: .vframepsp outside of prologue
+.*:30: Error: .spill outside of prologue
+.*:31: Error: .restore outside of body region
+.*:32: Error: .save outside of prologue
+.*:33: Error: .savesp outside of prologue
+.*:34: Error: .savepsp outside of prologue
+.*:35: Error: .save.g outside of prologue
+.*:36: Error: .save.gf outside of prologue
+.*:37: Error: .save.f outside of prologue
+.*:38: Error: .save.b outside of prologue
+.*:39: Error: .altrp outside of prologue
+.*:44: Error: .prologue within prologue
+.*:52: Error: .body outside of procedure
+.*:59: Warning: Initial .prologue.*
+.*:66: Warning: Initial .body.*
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/unwind-err.s	2005-02-08 10:36:18.000000000 +0100
+++ 2005-05-18/gas/testsuite/gas/ia64/unwind-err.s	2005-05-20 16:41:30.000000000 +0200
@@ -25,6 +25,8 @@ start:
 .copy_state 1
 .fframe 0
 .vframe r0
+.vframesp 0
+.vframepsp 0
 .spill 0
 .restore sp
 .save rp, r0
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/unwind-ok.d	1970-01-01 01:00:00.000000000 +0100
+++ 2005-05-18/gas/testsuite/gas/ia64/unwind-ok.d	2005-05-20 17:03:06.000000000 +0200
@@ -0,0 +1,224 @@
+#readelf: -u
+#name: ia64 unwind descriptors
+
+Unwind section '\.IA_64\.unwind' at offset 0x[[:xdigit:]]+ contains 8 entries:
+
+<full1>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x3 \( ?ehandler uhandler\), len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=8\)
+[[:space:]]*P6:fr_mem\(frmask=\[f2,f5\]\)
+[[:space:]]*P6:gr_mem\(grmask=\[r4,r7\]\)
+[[:space:]]*P1:br_mem\(brmask=\[b1,b5\]\)
+[[:space:]]*P4:spill_mask\(imask=\[rfb,rfb,--\]\)
+[[:space:]]*P7:spill_base\(pspoff=0x10-0x10\)
+[[:space:]]*P3:rp_br\(reg=b7\)
+[[:space:]]*P10:unwabi\(abi=@svr4,context=0x00\)
+[[:space:]]*R1:body\(rlen=[[:digit:]]+\)
+[[:space:]]*X2:spill_reg\(t=0,reg=r4,treg=r2\)
+[[:space:]]*X4:spill_reg_p\(qp=p1,t=1,reg=r7,treg=r31\)
+[[:space:]]*X1:spill_sprel\(reg=b1,t=2,spoff=0x8\)
+[[:space:]]*X3:spill_sprel_p\(qp=p2,t=3,reg=b5,spoff=0x10\)
+[[:space:]]*X1:spill_psprel\(reg=f2,t=4,pspoff=0x10-0x28\)
+[[:space:]]*X3:spill_psprel_p\(qp=p4,t=5,reg=f5,pspoff=0x10-0x30\)
+[[:space:]]*X2:restore\(t=6,reg=f16\)
+[[:space:]]*X4:restore_p\(qp=p8,t=7,reg=f31\)
+[[:space:]]*X2:spill_reg\(t=8,reg=ar\.bsp,treg=r16\)
+[[:space:]]*X2:spill_reg\(t=9,reg=ar\.bspstore,treg=r17\)
+[[:space:]]*X2:spill_reg\(t=10,reg=ar\.fpsr,treg=r18\)
+[[:space:]]*X2:spill_reg\(t=11,reg=ar\.lc,treg=r19\)
+[[:space:]]*X2:spill_reg\(t=12,reg=ar\.pfs,treg=r20\)
+[[:space:]]*X2:spill_reg\(t=13,reg=ar\.rnat,treg=r21\)
+[[:space:]]*X2:spill_reg\(t=14,reg=ar\.unat,treg=r22\)
+[[:space:]]*X2:spill_reg\(t=15,reg=psp,treg=r23\)
+[[:space:]]*X2:spill_reg\(t=16,reg=pr,treg=r24\)
+[[:space:]]*X2:spill_reg\(t=17,reg=rp,treg=r25\)
+[[:space:]]*X2:spill_reg\(t=18,reg=@priunat,treg=r26\)
+[[:space:]]*B1:label_state\(label=1\)
+[[:space:]]*B2:epilogue\(t=4,ecount=0\)
+[[:space:]]*B1:copy_state\(label=1\)
+#...
+<full2>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R2:prologue_gr\(mask=\[rp,psp,pr\],grsave=r8,rlen=14\)
+[[:space:]]*P5:frgr_mem\(grmask=\[r4,r7\],frmask=\[f2,f31\]\)
+[[:space:]]*P4:spill_mask\(imask=\[b-b,bb-,---,---,--\]\)
+[[:space:]]*P7:spill_base\(pspoff=0x10-0x10\)
+[[:space:]]*P2:br_gr\(brmask=\[b1,b5\],gr=r32\)
+[[:space:]]*X2:spill_reg\(t=6,reg=f31,treg=f31\)
+[[:space:]]*X4:spill_reg_p\(qp=p63,t=7,reg=f16,treg=f0\)
+[[:space:]]*X1:spill_sprel\(reg=f5,t=8,spoff=0x20\)
+[[:space:]]*X3:spill_sprel_p\(qp=p31,t=9,reg=f2,spoff=0x18\)
+[[:space:]]*X1:spill_psprel\(reg=b5,t=10,pspoff=0x10-0x20\)
+[[:space:]]*X3:spill_psprel_p\(qp=p15,t=11,reg=b1,pspoff=0x10-0x18\)
+[[:space:]]*X2:restore\(t=12,reg=r7\)
+[[:space:]]*X4:restore_p\(qp=p7,t=13,reg=r4\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=7\)
+[[:space:]]*B4:label_state\(label=32\)
+[[:space:]]*B3:epilogue\(t=4,ecount=32\)
+[[:space:]]*B4:copy_state\(label=32\)
+#...
+<full3>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R3:prologue\(rlen=33\)
+[[:space:]]*P4:spill_mask\(imask=\[rrb,brr,bb-,---,---,---,---,---,---,---,---\]\)
+[[:space:]]*P7:spill_base\(pspoff=0x10-0x10\)
+[[:space:]]*P9:gr_gr\(grmask=\[r4,r5\],r32\)
+[[:space:]]*P2:br_gr\(brmask=\[b1,b2\],gr=r34\)
+[[:space:]]*P9:gr_gr\(grmask=\[r6,r7\],r124\)
+[[:space:]]*P2:br_gr\(brmask=\[b4,b5\],gr=r126\)
+[[:space:]]*R3:body\(rlen=33\)
+#...
+<fframe>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=1\)
+[[:space:]]*P7:mem_stack_f\(t=0,size=0\)
+[[:space:]]*R1:body\(rlen=2\)
+#...
+<vframe>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=11\)
+[[:space:]]*P7:mem_stack_v\(t=0\)
+[[:space:]]*P3:psp_gr\(reg=r16\)
+[[:space:]]*P8:bsp_when\(t=1\)
+[[:space:]]*P3:bsp_gr\(reg=r17\)
+[[:space:]]*P8:bspstore_when\(t=2\)
+[[:space:]]*P3:bspstore_gr\(reg=r18\)
+[[:space:]]*P7:fpsr_when\(t=3\)
+[[:space:]]*P3:fpsr_gr\(reg=r19\)
+[[:space:]]*P7:lc_when\(t=4\)
+[[:space:]]*P3:lc_gr\(reg=r20\)
+[[:space:]]*P7:pfs_when\(t=5\)
+[[:space:]]*P3:pfs_gr\(reg=r21\)
+[[:space:]]*P8:rnat_when\(t=6\)
+[[:space:]]*P3:rnat_gr\(reg=r22\)
+[[:space:]]*P7:unat_when\(t=7\)
+[[:space:]]*P3:unat_gr\(reg=r23\)
+[[:space:]]*P7:pr_when\(t=8\)
+[[:space:]]*P3:pr_gr\(reg=r24\)
+[[:space:]]*P8:priunat_when_gr\(t=9\)
+[[:space:]]*P3:priunat_gr\(reg=r25\)
+[[:space:]]*P7:rp_when\(t=10\)
+[[:space:]]*P3:rp_gr\(reg=r26\)
+[[:space:]]*R1:body\(rlen=1\)
+#...
+<vframesp>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=11\)
+[[:space:]]*P7:mem_stack_v\(t=0\)
+[[:space:]]*P7:psp_sprel\(spoff=0x0\)
+[[:space:]]*P8:bsp_when\(t=1\)
+[[:space:]]*P8:bsp_sprel\(spoff=0x8\)
+[[:space:]]*P8:bspstore_when\(t=2\)
+[[:space:]]*P8:bspstore_sprel\(spoff=0x10\)
+[[:space:]]*P7:fpsr_when\(t=3\)
+[[:space:]]*P8:fpsr_sprel\(spoff=0x18\)
+[[:space:]]*P7:lc_when\(t=4\)
+[[:space:]]*P8:lc_sprel\(spoff=0x20\)
+[[:space:]]*P7:pfs_when\(t=5\)
+[[:space:]]*P8:pfs_sprel\(spoff=0x28\)
+[[:space:]]*P8:rnat_when\(t=6\)
+[[:space:]]*P8:rnat_sprel\(spoff=0x30\)
+[[:space:]]*P7:unat_when\(t=7\)
+[[:space:]]*P8:unat_sprel\(spoff=0x38\)
+[[:space:]]*P7:pr_when\(t=8\)
+[[:space:]]*P8:pr_sprel\(spoff=0x40\)
+[[:space:]]*P8:priunat_when_mem\(t=9\)
+[[:space:]]*P8:priunat_sprel\(spoff=0x48\)
+[[:space:]]*P7:rp_when\(t=10\)
+[[:space:]]*P8:rp_sprel\(spoff=0x50\)
+[[:space:]]*R1:body\(rlen=1\)
+#...
+<vframepsp>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=11\)
+[[:space:]]*P7:mem_stack_v\(t=0\)
+[[:space:]]*P8:psp_psprel\(pspoff=0x10-0x10\)
+[[:space:]]*P8:bsp_when\(t=1\)
+[[:space:]]*P8:bsp_psprel\(pspoff=0x10-0x18\)
+[[:space:]]*P8:bspstore_when\(t=2\)
+[[:space:]]*P8:bspstore_psprel\(pspoff=0x10-0x20\)
+[[:space:]]*P7:fpsr_when\(t=3\)
+[[:space:]]*P7:fpsr_psprel\(pspoff=0x10-0x28\)
+[[:space:]]*P7:lc_when\(t=4\)
+[[:space:]]*P7:lc_psprel\(pspoff=0x10-0x30\)
+[[:space:]]*P7:pfs_when\(t=5\)
+[[:space:]]*P7:pfs_psprel\(pspoff=0x10-0x38\)
+[[:space:]]*P8:rnat_when\(t=6\)
+[[:space:]]*P8:rnat_psprel\(pspoff=0x10-0x40\)
+[[:space:]]*P7:unat_when\(t=7\)
+[[:space:]]*P7:unat_psprel\(pspoff=0x10-0x48\)
+[[:space:]]*P7:pr_when\(t=8\)
+[[:space:]]*P7:pr_psprel\(pspoff=0x10-0x50\)
+[[:space:]]*P8:priunat_when_mem\(t=9\)
+[[:space:]]*P8:priunat_psprel\(pspoff=0x10-0x58\)
+[[:space:]]*P7:rp_when\(t=10\)
+[[:space:]]*P7:rp_psprel\(pspoff=0x10-0x60\)
+[[:space:]]*R1:body\(rlen=1\)
+#...
+<simple>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+#pass
--- /home/jbeulich/src/binutils/mainline/2005-05-18/gas/testsuite/gas/ia64/unwind-ok.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-05-18/gas/testsuite/gas/ia64/unwind-ok.s	2005-05-20 11:02:35.000000000 +0200
@@ -0,0 +1,272 @@
+.text
+.proc personality
+personality:
+	br.ret.sptk	rp
+.endp personality
+
+.proc full1
+full1:
+
+.prologue
+.spill 0
+.save.g 0x1
+	nop		0
+.save.f 0x1
+	nop		0
+.save.b 0x01
+	nop		0
+.save.g 0x8
+	nop		0
+.save.f 0x8
+	nop		0
+.save.b 0x10
+	nop		0
+.altrp b7
+	nop		0
+.unwabi @svr4, 0
+	nop		0
+
+.body
+.spillreg r4, r2
+	nop		0
+.spillreg.p p1, r7, r127
+	nop		0
+.spillsp b1, 0x08
+	nop		0
+.spillsp.p p2, b5, 0x10
+	nop		0
+.spillpsp f2, 0x18
+	nop		0
+.spillpsp.p p4, f5, 0x20
+	nop		0
+.restorereg f16
+	nop		0
+.restorereg.p p8, f31
+	nop		0
+
+.spillreg ar.bsp, r16
+	nop		0
+.spillreg ar.bspstore, r17
+	nop		0
+.spillreg ar.fpsr, r18
+	nop		0
+.spillreg ar.lc, r19
+	nop		0
+.spillreg ar.pfs, r20
+	nop		0
+.spillreg ar.rnat, r21
+	nop		0
+.spillreg ar.unat, r22
+	nop		0
+.spillreg psp, r23
+	nop		0
+.spillreg pr, r24
+	nop		0
+.spillreg rp, r25
+	nop		0
+.spillreg @priunat, r26
+	nop		0
+
+.label_state 1
+	nop		0
+.restore sp
+	nop.x		0
+.copy_state 1
+	br.ret.sptk	rp
+
+.personality personality
+.handlerdata
+	data4		-1
+	data4		0
+
+.endp full1
+
+.proc full2
+full2:
+
+.prologue 0xb, r8
+.spill 0
+.save.gf 0x1, 0x00001
+	nop		0
+	nop		0
+.save.b 0x11, r32
+	nop		0
+	nop		0
+.save.gf 0x8, 0x80000
+	nop		0
+	nop		0
+.spillreg f31, f127
+	nop		0
+.spillreg.p p63, f16, f32
+	nop		0
+.spillsp f5, 0x20
+	nop		0
+.spillsp.p p31, f2, 0x18
+	nop		0
+.spillpsp b5, 0x10
+	nop		0
+.spillpsp.p p15, b1, 0x08
+	nop		0
+.restorereg r7
+	nop		0
+.restorereg.p p7, r4
+	nop		0
+
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+
+.body
+.label_state 32
+	nop		0
+.restore sp, 32
+	nop.x		0
+.copy_state 32
+	br.ret.sptk	rp
+.endp full2
+
+.proc full3
+full3:
+
+.prologue
+.spill 0
+.save.g 0x3, r32
+	nop		0
+	nop		0
+.save.b 0x03, r34
+	nop		0
+	nop		0
+.save.g 0xc, r124
+	nop		0
+	nop		0
+.save.b 0x18, r126
+	nop		0
+	nop		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+.body
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	br.ret.sptk	rp
+.endp full3
+
+.proc fframe
+fframe:
+.prologue
+.fframe 0
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp fframe
+
+.proc vframe
+vframe:
+.prologue
+.vframe r16
+	nop		0
+.save ar.bsp, r17
+	nop		0
+.save ar.bspstore, r18
+	nop		0
+.save ar.fpsr, r19
+	nop		0
+.save ar.lc, r20
+	nop		0
+.save ar.pfs, r21
+	nop		0
+.save ar.rnat, r22
+	nop		0
+.save ar.unat, r23
+	nop		0
+.save pr, r24
+	nop		0
+.save @priunat, r25
+	nop		0
+.save rp, r26
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp vframe
+
+.proc vframesp
+vframesp:
+.prologue
+.vframesp 0
+	nop		0
+.savesp ar.bsp, 0x08
+	nop		0
+.savesp ar.bspstore, 0x10
+	nop		0
+.savesp ar.fpsr, 0x18
+	nop		0
+.savesp ar.lc, 0x20
+	nop		0
+.savesp ar.pfs, 0x28
+	nop		0
+.savesp ar.rnat, 0x30
+	nop		0
+.savesp ar.unat, 0x38
+	nop		0
+.savesp pr, 0x40
+	nop		0
+.savesp @priunat, 0x48
+	nop		0
+.savesp rp, 0x50
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp vframesp
+
+.proc vframepsp
+vframepsp:
+.prologue
+.vframepsp 0
+	nop		0
+.savepsp ar.bsp, 0x08
+	nop		0
+.savepsp ar.bspstore, 0x10
+	nop		0
+.savepsp ar.fpsr, 0x18
+	nop		0
+.savepsp ar.lc, 0x20
+	nop		0
+.savepsp ar.pfs, 0x28
+	nop		0
+.savepsp ar.rnat, 0x30
+	nop		0
+.savepsp ar.unat, 0x38
+	nop		0
+.savepsp pr, 0x40
+	nop		0
+.savepsp @priunat, 0x48
+	nop		0
+.savepsp rp, 0x50
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp vframepsp
+
+.proc simple
+simple:
+.unwentry
+	br.ret.sptk	rp
+.endp simple

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] ia64: unwind directive handling
  2005-05-23 10:19 [PATCH] ia64: unwind directive handling Jan Beulich
@ 2005-06-10  0:07 ` James E Wilson
  0 siblings, 0 replies; 8+ messages in thread
From: James E Wilson @ 2005-06-10  0:07 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

[-- Attachment #1: Type: text/plain, Size: 465 bytes --]

On Mon, 2005-05-23 at 03:00, Jan Beulich wrote:
> The main purpose of this patch is to add parser level support for the
> optional tags various of the unwind directives permit and to make '}' a
> statement separator as is done in ias. Along with that it consolidates
> some redundant functionality, tightens operand checking for many unwind
> directives, and fixes some other bugs.

Comments attached.
-- 
Jim Wilson, GNU Tools Support, http://www.SpecifixInc.com


[-- Attachment #2: patch.comments --]
[-- Type: text/plain, Size: 4874 bytes --]

For .prologue, the assembler docs appear to be ambiguous.  It states that the
second operand to .prologue is grsave, and then in the following section it
defines what grsave means, but no where does it clearly define how grsave is
represented in the assembler file.  Still, it does seem a bit odd that we ended
up using a constant instead of a register name.  That was probably a historical
mistake.

You are changing unwind-ia64.c.  There are also copies of this code in the
linux kernel and in the libunwind package.  So if we change this file, we need
to let David Mosberger know, so he can fix the other two occurances.  And there
is also the gcc unwinder that would need to be fixed.  There is the more
general question of whether any unwinder supports psp_psprel.  If no unwinder
supports it, then it doesn't serve much purpose for gas to support it.

I checked the SCRA.  Appendix B constains tables listing all of the valid
unwind encodings.  This does not include psp_psprel.  I see that it comes
from the .vframepsp directive.  Curiously, the asm language manual has two
typos in the vframepsp docs.  It emits two unwind records, and both of them
are mispelled.  Maybe this is a late change that was never properly documented.
How do you know that P8 r=0 is correct?  You confirmed this from ias?  It would
be useful to get confirmation that this is actually supposed to exist, and
that we are actually supposed to implement it.  We should report these doc
bugs to Intel.  The asm language manual has so many bugs that probably no one
cares about it, but bugs in the SCRA are serious, as it is used for ABI
conformance.  The SCRA must be fixed if it is wrong.

As for '}' as a line separator, as usual, this isn't documented in the
asm language manual.  Unfortunately, this means that we are now handling '{'
and '}' differently.  This begs the question of whether perhaps '{' can also
be a line separator.  Did you try that?  If so, then that would allow us to
handle them both the same way again.

Putting a switch default case in the middle looks odd to me.  Typically, it is
at the end, after all of the cases.  That makes it easier to read the code,
since it is easy to see that the default case is there.  This is done in
dot_save and dot_savemem.

I notice that you allow r0 as a valid for a gr-location in a .save directive.
Does this ever make sense?  I noticed this because you deliberately disallow
r0 in other places, such as the first option to .spillreg, but checking the
docs, I see that the docs explicitly say that only certain registers are valid
here.  However, the docs don't appear to say anything about using r0 for the
destination (gr-location) of a .save directive.  This is probably OK as it is.

I see you are adding a popcnt function into the middle of the tc-ia64.c file.
This does look a little wierd.  I don't have any strong objection to it, except
to point out that the GNU coding standards require that every function have
an explanatory comment that describes what the function does and you didn't
add one.

In dot_saveg, you have
   grmask = e.X_add_number;
then you test the value in e.X_add_number.  I think that makes more sense as
tests again grmask, and that saves some memory references.  Similarly in
dot_saveb.

In dot_spillmem, you have what appears to be an obvious bug.
+    po = (psprel = ~psprel) ? "spillpsp.p" : "spillsp.p";
I realize it isn't a bug, but the next person looking at the code might think
otherwise.  The assignment should be a separate statement to avoid confusion.

The "first" code in ia64_start_line needs a comment explaining why it is there.
You put this in the Changelog entry, but the "why" info always belongs
in the source code, not the ChangeLog file.  The "what" info goes in the
ChangeLog.

I see that you have bugs documented as comments in the file
gas/testsuite/gas/ia64/unwind-bad.l.  That is a very unusual place and way
to document bugs.  It would be better if this was documented someplace more
visible.  Probably no one is ever going to notice that the bugs are documented
here.  I'm also left wondering how you generated this file.  How do you know
that these error messages are missing?  What does the "previous spill
incomplete" message mean?  It looks almost like you were copying from
something else, which might be copyright infringement.  

I noticed that you change unwabi from in_procedure to in_prologue.  Do you have
a reason for that?  I also noticed that unwabi is missing from the SCRA, which
is a serious bug, though the linux kernel and libunwind already support unwabi
so that isn't a problem.

Otherwise this looks reasonable to me.  I think most of these issues are
pretty minor except the psp_psprel, and perhaps the unwind-bad.l issue
(depending on what the answer is).

I'm going to do test builds of the linux kernel, glibc, and gcc to check for
problems.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] ia64: unwind directive handling
  2005-06-27 10:31 Jan Beulich
@ 2005-07-01  3:10 ` James E Wilson
  0 siblings, 0 replies; 8+ messages in thread
From: James E Wilson @ 2005-07-01  3:10 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On Mon, 2005-06-27 at 03:32, Jan Beulich wrote:
> Here's the updated patch, with (I believe) all issues addressed (except for where I gave reasons for why things were done the way they are in an earlier reply). Additionally to that there are four more new warnings (noting inconsistencies between .prologue and later .save-s).

Sorry about the delays.  I'm hoping to be more responsive now that the
GCC Summit is over.  It messed up my schedule the last few weeks.

I have no problem with any of the answers you've given, or the
subsequent changes you made.

About the default in the middle of the switch statement issue, I missed
the fact that there was a missing break.  There is a relatively common
convention to add a /* FALLTHOUGH */ marker in this case.  This makes it
obvious that the missing break is intentional, and not a bug.  It also
makes it obvious that the case labels can't be reordered without
breaking the code.  Anyways, in this case, I think the rewrite is
better.  Your original code was too clever for its own good.  This
invariably leads to problems, as at some point in the future someone you
don't know will fail to understand how clever the code is, assume it is
broken, and then proceed to fix it.  Clear code is better than clever
code, even if it is a few lines longer.  And yes, gotos should be
avoided.

I said I was going to do some testing with the first version of the
patch.  glibc built OK.  I found two minor typos in the linux kernel,
spurious characters at the end of an unwind directive that were now
caught whereas they used to be ignored.  Both have been reported, and I
think both have already been fixed.  In any case, there is nothing to
worry about here.  I may have forgetten to do the gcc bootstrap.  I
don't recall.

The patch is OK.
-- 
Jim Wilson, GNU Tools Support, http://www.SpecifixInc.com


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] ia64: unwind directive handling
@ 2005-06-27 10:31 Jan Beulich
  2005-07-01  3:10 ` James E Wilson
  0 siblings, 1 reply; 8+ messages in thread
From: Jan Beulich @ 2005-06-27 10:31 UTC (permalink / raw)
  To: James Wilson; +Cc: binutils

[-- Attachment #1: Type: text/plain, Size: 3942 bytes --]

>>> James E Wilson <wilson@specifixinc.com> 10.06.05 02:06:49 >>>
>On Mon, 2005-05-23 at 03:00, Jan Beulich wrote:
>> The main purpose of this patch is to add parser level support for the
>> optional tags various of the unwind directives permit and to make '}' a
>> statement separator as is done in ias. Along with that it consolidates
>> some redundant functionality, tightens operand checking for many unwind
>> directives, and fixes some other bugs.
>
>Comments attached.

Here's the updated patch, with (I believe) all issues addressed (except for where I gave reasons for why things were done the way they are in an earlier reply). Additionally to that there are four more new warnings (noting inconsistencies between .prologue and later .save-s).

Built and tested on ia64-unknown-linux-gnu.

Jan

gas/
2005-06-27  Jan Beulich  <jbeulich@novell.com>

	* config/tc-ia64.c (line_separator_chars): Add '{' and '}'.
	(output_spill_psprel, output_spill_psprel_p): Combine.
	(output_spill_sprel, output_spill_sprel_p): Combine.
	(output_spill_reg, output_spill_regp_p): Combine.
	(process_one_record): Handle psp_psprel.
	(parse_predicate_and_operand): New.
	(convert_expr_to_ab_reg): Two new parameters. Return void. Always
	initialize output values. Emit diagnostic case here.
	(convert_expr_to_xy_reg): Likewise. Don't allow r0, f0, and f1.
	(add_unwind_entry): New second parameter. Allow first parameter to
	be NULL. Parse optional tag, emit warning about further support for
	it otherwise being missing. Check end-of-line when requested.
	(dot_fframe): Clear operand when wrong. Allow tag.
	(dot_vframe): Likewise.
	(dot_vframesp): Likewise. Rename parameter, issue warning when psp
	relative.
	(dot_vframepsp): Remove.
	(dot_altrp): Clear operand when wrong. Allow tag.
	(dot_save): Likewise. Let default case also go through
	add_unwind_entry.
	(dot_savemem): Likewise.
	(dot_restore): Don't return when wrong operand. Allow tag.
	(dot_spillreg, dot_spillreg_p): Combine. Simplify by using
	parse_predicate_and_operand and the new arguments to
	convert_expr_to_ab_reg and convert_expr_to_xy_reg. Don't return
	when wrong operand. Allow tag.
	(dot_restorereg, dot_restorereg_p): Likewise.
	(dot_spillmem, dot_spillmem_p): Likewise.
	(dot_saveg): Clear operand when wrong. Perform tighter operand
	checks. Allow tag.
	(dot_savef): Likewise.
	(dot_saveb): Likewise.
	(dot_savegf): Likewise.
	(dot_spill): Remove end-of-line check. 	Combine. Simplify by using
	parse_predicate_and_operand and the new arguments to
	convert_expr_to_ab_reg and convert_expr_to_xy_reg. Don't return
	when wrong operand. Allow tag.
	(popcount): New.
	(dot_label_state): Don't return when wrong operand.
	(dot_copy_state): Likewise.
	(dot_unwabi): Likewise. Check if in prologue.
	(dot_body): Don't call demand_empty_rest_of_line.
	(dot_prologue): Type of mask and grsave is unsigned. Perform tighter
	operand checks.
	(md_pseudo_table): Also use dot_restorereg for .restorereg.p. Also
	use dot_spillreg for .spillreg.p. Also use dot_spillmem for
	.spillpsp.p and .spillsp.p. Also use dot_vframesp for .vframepsp.
	(parse_operand): New second parameter. Don't deal with '}' here
	anymore. Don't advance past end-of-line.
	(parse_operands): Pass second argument to parse_operand.
	(ia64_start_line): Prevent out-of-bounds access through
	input_line_pointer. Deal with '}' here.
	(ia64_unrecognized_line): Don't deal with '}' here.
	(dot_alias): Use ignore_rest_of_line not its deprecated alias
	discard_rest_of_line.

gas/testsuite/
2005-06-27  Jan Beulich  <jbeulich@novell.com>

	* gas/ia64/group-2.s: Use register as second operand of .prologue.
	* gas/ia64/unwind-err.s: Add check for .vframesp.
	* gas/ia64/unwind-err.l: Adjust.
	* gas/ia64/strange.[sd]: New.
	* gas/ia64/unwind-bad.[sl]: New.
	* gas/ia64/unwind-ok.[sd]: New.
	* gas/ia64/ia64.exp: Run new tests.




[-- Attachment #2: binutils-mainline-ia64-unwind.patch --]
[-- Type: application/octet-stream, Size: 77614 bytes --]

Built and tested on ia64-unknown-linux-gnu.

Jan

gas/
2005-06-27  Jan Beulich  <jbeulich@novell.com>

	* config/tc-ia64.c (line_separator_chars): Add '{' and '}'.
	(output_spill_psprel, output_spill_psprel_p): Combine.
	(output_spill_sprel, output_spill_sprel_p): Combine.
	(output_spill_reg, output_spill_regp_p): Combine.
	(process_one_record): Handle psp_psprel.
	(parse_predicate_and_operand): New.
	(convert_expr_to_ab_reg): Two new parameters. Return void. Always
	initialize output values. Emit diagnostic case here.
	(convert_expr_to_xy_reg): Likewise. Don't allow r0, f0, and f1.
	(add_unwind_entry): New second parameter. Allow first parameter to
	be NULL. Parse optional tag, emit warning about further support for
	it otherwise being missing. Check end-of-line when requested.
	(dot_fframe): Clear operand when wrong. Allow tag.
	(dot_vframe): Likewise.
	(dot_vframesp): Likewise. Rename parameter, issue warning when psp
	relative.
	(dot_vframepsp): Remove.
	(dot_altrp): Clear operand when wrong. Allow tag.
	(dot_save): Likewise. Let default case also go through
	add_unwind_entry.
	(dot_savemem): Likewise.
	(dot_restore): Don't return when wrong operand. Allow tag.
	(dot_spillreg, dot_spillreg_p): Combine. Simplify by using
	parse_predicate_and_operand and the new arguments to
	convert_expr_to_ab_reg and convert_expr_to_xy_reg. Don't return
	when wrong operand. Allow tag.
	(dot_restorereg, dot_restorereg_p): Likewise.
	(dot_spillmem, dot_spillmem_p): Likewise.
	(dot_saveg): Clear operand when wrong. Perform tighter operand
	checks. Allow tag.
	(dot_savef): Likewise.
	(dot_saveb): Likewise.
	(dot_savegf): Likewise.
	(dot_spill): Remove end-of-line check. 	Combine. Simplify by using
	parse_predicate_and_operand and the new arguments to
	convert_expr_to_ab_reg and convert_expr_to_xy_reg. Don't return
	when wrong operand. Allow tag.
	(popcount): New.
	(dot_label_state): Don't return when wrong operand.
	(dot_copy_state): Likewise.
	(dot_unwabi): Likewise. Check if in prologue.
	(dot_body): Don't call demand_empty_rest_of_line.
	(dot_prologue): Type of mask and grsave is unsigned. Perform tighter
	operand checks.
	(md_pseudo_table): Also use dot_restorereg for .restorereg.p. Also
	use dot_spillreg for .spillreg.p. Also use dot_spillmem for
	.spillpsp.p and .spillsp.p. Also use dot_vframesp for .vframepsp.
	(parse_operand): New second parameter. Don't deal with '}' here
	anymore. Don't advance past end-of-line.
	(parse_operands): Pass second argument to parse_operand.
	(ia64_start_line): Prevent out-of-bounds access through
	input_line_pointer. Deal with '}' here.
	(ia64_unrecognized_line): Don't deal with '}' here.
	(dot_alias): Use ignore_rest_of_line not its deprecated alias
	discard_rest_of_line.

gas/testsuite/
2005-06-27  Jan Beulich  <jbeulich@novell.com>

	* gas/ia64/group-2.s: Use register as second operand of .prologue.
	* gas/ia64/unwind-err.s: Add check for .vframesp.
	* gas/ia64/unwind-err.l: Adjust.
	* gas/ia64/strange.[sd]: New.
	* gas/ia64/unwind-bad.[sl]: New.
	* gas/ia64/unwind-ok.[sd]: New.
	* gas/ia64/ia64.exp: Run new tests.

--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/config/tc-ia64.c	2005-06-16 10:42:17.000000000 +0200
+++ 2005-06-27/gas/config/tc-ia64.c	2005-06-27 12:04:25.918876223 +0200
@@ -193,7 +193,7 @@ const char line_comment_chars[] = "#";
 
 /* Characters which may be used to separate multiple commands on a
    single line.  */
-const char line_separator_chars[] = ";";
+const char line_separator_chars[] = ";{}";
 
 /* Characters which are used to indicate an exponent in a floating
    point number.  */
@@ -736,6 +736,7 @@ static struct
   /* TRUE if processing unwind directives in a prologue region.  */
   unsigned int prologue : 1;
   unsigned int prologue_mask : 4;
+  unsigned int prologue_gr : 7;
   unsigned int body : 1;
   unsigned int insn : 1;
   unsigned int prologue_count;	/* number of .prologues seen so far */
@@ -762,11 +763,9 @@ static void dot_proc PARAMS ((int));
 static void dot_fframe PARAMS ((int));
 static void dot_vframe PARAMS ((int));
 static void dot_vframesp PARAMS ((int));
-static void dot_vframepsp PARAMS ((int));
 static void dot_save PARAMS ((int));
 static void dot_restore PARAMS ((int));
 static void dot_restorereg PARAMS ((int));
-static void dot_restorereg_p PARAMS ((int));
 static void dot_handlerdata  PARAMS ((int));
 static void dot_unwentry PARAMS ((int));
 static void dot_altrp PARAMS ((int));
@@ -778,8 +777,6 @@ static void dot_savegf PARAMS ((int));
 static void dot_spill PARAMS ((int));
 static void dot_spillreg PARAMS ((int));
 static void dot_spillmem PARAMS ((int));
-static void dot_spillreg_p PARAMS ((int));
-static void dot_spillmem_p PARAMS ((int));
 static void dot_label_state PARAMS ((int));
 static void dot_copy_state PARAMS ((int));
 static void dot_unwabi PARAMS ((int));
@@ -809,14 +806,14 @@ static void dot_serialize PARAMS ((int))
 static void dot_dv_mode PARAMS ((int));
 static void dot_entry PARAMS ((int));
 static void dot_mem_offset PARAMS ((int));
-static void add_unwind_entry PARAMS((unw_rec_list *ptr));
+static void add_unwind_entry PARAMS((unw_rec_list *, int));
 static symbolS *declare_register PARAMS ((const char *name, int regnum));
 static void declare_register_set PARAMS ((const char *, int, int));
 static unsigned int operand_width PARAMS ((enum ia64_opnd));
 static enum operand_match_result operand_match PARAMS ((const struct ia64_opcode *idesc,
 							int index,
 							expressionS *e));
-static int parse_operand PARAMS ((expressionS *e));
+static int parse_operand PARAMS ((expressionS *, int));
 static struct ia64_opcode * parse_operands PARAMS ((struct ia64_opcode *));
 static void build_insn PARAMS ((struct slot *, bfd_vma *));
 static void emit_one_bundle PARAMS ((void));
@@ -937,15 +934,11 @@ static unw_rec_list *output_unwabi PARAM
 static unw_rec_list *output_epilogue PARAMS ((unsigned long));
 static unw_rec_list *output_label_state PARAMS ((unsigned long));
 static unw_rec_list *output_copy_state PARAMS ((unsigned long));
-static unw_rec_list *output_spill_psprel PARAMS ((unsigned int, unsigned int, unsigned int));
-static unw_rec_list *output_spill_sprel PARAMS ((unsigned int, unsigned int, unsigned int));
-static unw_rec_list *output_spill_psprel_p PARAMS ((unsigned int, unsigned int, unsigned int,
+static unw_rec_list *output_spill_psprel PARAMS ((unsigned int, unsigned int, unsigned int,
 						    unsigned int));
-static unw_rec_list *output_spill_sprel_p PARAMS ((unsigned int, unsigned int, unsigned int,
+static unw_rec_list *output_spill_sprel PARAMS ((unsigned int, unsigned int, unsigned int,
 						   unsigned int));
 static unw_rec_list *output_spill_reg PARAMS ((unsigned int, unsigned int, unsigned int,
-					       unsigned int));
-static unw_rec_list *output_spill_reg_p PARAMS ((unsigned int, unsigned int, unsigned int,
 						 unsigned int, unsigned int));
 static void process_one_record PARAMS ((unw_rec_list *, vbyte_func));
 static void process_unw_records PARAMS ((unw_rec_list *, vbyte_func));
@@ -956,8 +949,9 @@ static unsigned long slot_index PARAMS (
 					 int));
 static unw_rec_list *optimize_unw_records PARAMS ((unw_rec_list *));
 static void fixup_unw_records PARAMS ((unw_rec_list *, int));
-static int convert_expr_to_ab_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
-static int convert_expr_to_xy_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
+static int parse_predicate_and_operand PARAMS ((expressionS *, unsigned *, const char *));
+static void convert_expr_to_ab_reg PARAMS ((const expressionS *, unsigned int *, unsigned int *, const char *, int));
+static void convert_expr_to_xy_reg PARAMS ((const expressionS *, unsigned int *, unsigned int *, const char *, int));
 static unsigned int get_saved_prologue_count PARAMS ((unsigned long));
 static void save_prologue_count PARAMS ((unsigned long, unsigned int));
 static void free_saved_prologue_counts PARAMS ((void));
@@ -2288,39 +2282,13 @@ output_copy_state (unsigned long label)
 }
 
 static unw_rec_list *
-output_spill_psprel (ab, reg, offset)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int offset;
-{
-  unw_rec_list *ptr = alloc_record (spill_psprel);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_sprel (ab, reg, offset)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int offset;
-{
-  unw_rec_list *ptr = alloc_record (spill_sprel);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.spoff = offset / 4;
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_psprel_p (ab, reg, offset, predicate)
+output_spill_psprel (ab, reg, offset, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int offset;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_psprel_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_psprel_p : spill_psprel);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
   ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
@@ -2329,13 +2297,13 @@ output_spill_psprel_p (ab, reg, offset, 
 }
 
 static unw_rec_list *
-output_spill_sprel_p (ab, reg, offset, predicate)
+output_spill_sprel (ab, reg, offset, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int offset;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_sprel_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_sprel_p : spill_sprel);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
   ptr->r.record.x.spoff = offset / 4;
@@ -2344,29 +2312,14 @@ output_spill_sprel_p (ab, reg, offset, p
 }
 
 static unw_rec_list *
-output_spill_reg (ab, reg, targ_reg, xy)
-     unsigned int ab;
-     unsigned int reg;
-     unsigned int targ_reg;
-     unsigned int xy;
-{
-  unw_rec_list *ptr = alloc_record (spill_reg);
-  ptr->r.record.x.ab = ab;
-  ptr->r.record.x.reg = reg;
-  ptr->r.record.x.treg = targ_reg;
-  ptr->r.record.x.xy = xy;
-  return ptr;
-}
-
-static unw_rec_list *
-output_spill_reg_p (ab, reg, targ_reg, xy, predicate)
+output_spill_reg (ab, reg, targ_reg, xy, predicate)
      unsigned int ab;
      unsigned int reg;
      unsigned int targ_reg;
      unsigned int xy;
      unsigned int predicate;
 {
-  unw_rec_list *ptr = alloc_record (spill_reg_p);
+  unw_rec_list *ptr = alloc_record (predicate ? spill_reg_p : spill_reg);
   ptr->r.record.x.ab = ab;
   ptr->r.record.x.reg = reg;
   ptr->r.record.x.treg = targ_reg;
@@ -2577,6 +2530,28 @@ calc_record_size (list)
   return vbyte_count;
 }
 
+/* Return the number of bits set in the input value.
+   Perhaps this has a better place...  */
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# define popcount __builtin_popcount
+#else
+static int
+popcount (unsigned x)
+{
+  static const unsigned char popcnt[16] =
+    {
+      0, 1, 1, 2,
+      1, 2, 2, 3,
+      1, 2, 2, 3,
+      2, 3, 3, 4
+    };
+
+  if (x < NELEMS (popcnt))
+    return popcnt[x];
+  return popcnt[x % NELEMS (popcnt)] + popcount (x / NELEMS (popcnt));
+}
+#endif
+
 /* Update IMASK bitmask to reflect the fact that one or more registers
    of type TYPE are saved starting at instruction with index T.  If N
    bits are set in REGMASK, it is assumed that instructions T through
@@ -3000,17 +2975,43 @@ ia64_convert_frag (fragS *frag)
 }
 
 static int
-convert_expr_to_ab_reg (e, ab, regp)
-     expressionS *e;
+parse_predicate_and_operand (e, qp, po)
+     expressionS * e;
+     unsigned * qp;
+     const char * po;
+{
+  int sep = parse_operand (e, ',');
+
+  *qp = e->X_add_number - REG_P;
+  if (e->X_op != O_register || *qp > 63)
+    {
+      as_bad ("First operand to .%s must be a predicate", po);
+      *qp = 0;
+    }
+  else if (*qp == 0)
+    as_warn ("Pointless use of p0 as first operand to .%s", po);
+  if (sep == ',')
+    sep = parse_operand (e, ',');
+  else
+    e->X_op = O_absent;
+  return sep;
+}
+
+static void
+convert_expr_to_ab_reg (e, ab, regp, po, n)
+     const expressionS *e;
      unsigned int *ab;
      unsigned int *regp;
+     const char * po;
+     int n;
 {
-  unsigned int reg;
+  unsigned int reg = e->X_add_number;
+
+  *ab = *regp = 0; /* Anything valid is good here.  */
 
   if (e->X_op != O_register)
-    return 0;
+    reg = REG_GR; /* Anything invalid is good here.  */
 
-  reg = e->X_add_number;
   if (reg >= (REG_GR + 4) && reg <= (REG_GR + 7))
     {
       *ab = 0;
@@ -3045,31 +3046,33 @@ convert_expr_to_ab_reg (e, ab, regp)
 	case REG_AR + AR_LC:	*regp = 10; break;
 
 	default:
-	  return 0;
+	  as_bad ("Operand %d to .%s must be a preserved register", n, po);
+	  break;
 	}
     }
-  return 1;
 }
 
-static int
-convert_expr_to_xy_reg (e, xy, regp)
-     expressionS *e;
+static void
+convert_expr_to_xy_reg (e, xy, regp, po, n)
+     const expressionS *e;
      unsigned int *xy;
      unsigned int *regp;
+     const char * po;
+     int n;
 {
-  unsigned int reg;
+  unsigned int reg = e->X_add_number;
 
-  if (e->X_op != O_register)
-    return 0;
+  *xy = *regp = 0; /* Anything valid is good here.  */
 
-  reg = e->X_add_number;
+  if (e->X_op != O_register)
+    reg = REG_GR; /* Anything invalid is good here.  */
 
-  if (/* reg >= REG_GR && */ reg <= (REG_GR + 127))
+  if (reg >= (REG_GR + 1) && reg <= (REG_GR + 127))
     {
       *xy = 0;
       *regp = reg - REG_GR;
     }
-  else if (reg >= REG_FR && reg <= (REG_FR + 127))
+  else if (reg >= (REG_FR + 2) && reg <= (REG_FR + 127))
     {
       *xy = 1;
       *regp = reg - REG_FR;
@@ -3080,8 +3083,7 @@ convert_expr_to_xy_reg (e, xy, regp)
       *regp = reg - REG_BR;
     }
   else
-    return -1;
-  return 1;
+    as_bad ("Operand %d to .%s must be a writable register", n, po);
 }
 
 static void
@@ -3210,18 +3212,48 @@ in_body (const char *directive)
 }
 
 static void
-add_unwind_entry (ptr)
+add_unwind_entry (ptr, sep)
      unw_rec_list *ptr;
+     int sep;
 {
-  if (unwind.tail)
-    unwind.tail->next = ptr;
-  else
-    unwind.list = ptr;
-  unwind.tail = ptr;
+  if (ptr)
+    {
+      if (unwind.tail)
+	unwind.tail->next = ptr;
+      else
+	unwind.list = ptr;
+      unwind.tail = ptr;
+
+      /* The current entry can in fact be a chain of unwind entries.  */
+      if (unwind.current_entry == NULL)
+	unwind.current_entry = ptr;
+    }
 
   /* The current entry can in fact be a chain of unwind entries.  */
   if (unwind.current_entry == NULL)
     unwind.current_entry = ptr;
+
+  if (sep == ',')
+    {
+      /* Parse a tag permitted for the current directive.  */
+      int ch;
+
+      SKIP_WHITESPACE ();
+      ch = get_symbol_end ();
+      /* FIXME: For now, just issue a warning that this isn't implemented.  */
+      {
+	static int warned;
+
+	if (!warned)
+	  {
+	    warned = 1;
+	    as_warn ("Tags on unwind pseudo-ops aren't supported, yet");
+	  }
+      }
+      *input_line_pointer = ch;
+    }
+  if (sep != NOT_A_CHAR)
+    demand_empty_rest_of_line ();
 }
 
 static void
@@ -3229,16 +3261,19 @@ dot_fframe (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
+  int sep;
 
   if (!in_prologue ("fframe"))
     return;
 
-  parse_operand (&e);
+  sep = parse_operand (&e, ',');
 
   if (e.X_op != O_constant)
-    as_bad ("Operand to .fframe must be a constant");
-  else
-    add_unwind_entry (output_mem_stack_f (e.X_add_number));
+    {
+      as_bad ("First operand to .fframe must be a constant");
+      e.X_add_number = 0;
+    }
+  add_unwind_entry (output_mem_stack_f (e.X_add_number), sep);
 }
 
 static void
@@ -3247,58 +3282,47 @@ dot_vframe (dummy)
 {
   expressionS e;
   unsigned reg;
+  int sep;
 
   if (!in_prologue ("vframe"))
     return;
 
-  parse_operand (&e);
+  sep = parse_operand (&e, ',');
   reg = e.X_add_number - REG_GR;
-  if (e.X_op == O_register && reg < 128)
+  if (e.X_op != O_register || reg > 127)
     {
-      add_unwind_entry (output_mem_stack_v ());
-      if (! (unwind.prologue_mask & 2))
-	add_unwind_entry (output_psp_gr (reg));
+      as_bad ("First operand to .vframe must be a general register");
+      reg = 0;
     }
-  else
-    as_bad ("First operand to .vframe must be a general register");
+  add_unwind_entry (output_mem_stack_v (), sep);
+  if (! (unwind.prologue_mask & 2))
+    add_unwind_entry (output_psp_gr (reg), NOT_A_CHAR);
+  else if (reg != unwind.prologue_gr
+		  + (unsigned) popcount (unwind.prologue_mask & (-2 << 1)))
+    as_warn ("Operand of .vframe contradicts .prologue");
 }
 
 static void
-dot_vframesp (dummy)
-     int dummy ATTRIBUTE_UNUSED;
+dot_vframesp (psp)
+     int psp;
 {
   expressionS e;
+  int sep;
 
-  if (!in_prologue ("vframesp"))
-    return;
-
-  parse_operand (&e);
-  if (e.X_op == O_constant)
-    {
-      add_unwind_entry (output_mem_stack_v ());
-      add_unwind_entry (output_psp_sprel (e.X_add_number));
-    }
-  else
-    as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
-}
-
-static void
-dot_vframepsp (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  expressionS e;
+  if (psp)
+    as_warn (".vframepsp is meaningless, assuming .vframesp was meant");
 
-  if (!in_prologue ("vframepsp"))
+  if (!in_prologue ("vframesp"))
     return;
 
-  parse_operand (&e);
-  if (e.X_op == O_constant)
+  sep = parse_operand (&e, ',');
+  if (e.X_op != O_constant)
     {
-      add_unwind_entry (output_mem_stack_v ());
-      add_unwind_entry (output_psp_sprel (e.X_add_number));
+      as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
+      e.X_add_number = 0;
     }
-  else
-    as_bad ("Operand to .vframepsp must be a constant (psp-relative offset)");
+  add_unwind_entry (output_mem_stack_v (), sep);
+  add_unwind_entry (output_psp_sprel (e.X_add_number), NOT_A_CHAR);
 }
 
 static void
@@ -3306,106 +3330,115 @@ dot_save (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
+  unsigned reg1, reg2;
   int sep;
-  int reg1, reg2;
 
   if (!in_prologue ("save"))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    as_bad ("No second operand to .save");
-  sep = parse_operand (&e2);
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    sep = parse_operand (&e2, ',');
+  else
+    e2.X_op = O_absent;
 
   reg1 = e1.X_add_number;
-  reg2 = e2.X_add_number - REG_GR;
-
   /* Make sure its a valid ar.xxx reg, OR its br0, aka 'rp'.  */
-  if (e1.X_op == O_register)
+  if (e1.X_op != O_register)
     {
-      if (e2.X_op == O_register && reg2 >= 0 && reg2 < 128)
-	{
-	  switch (reg1)
-	    {
-	    case REG_AR + AR_BSP:
-	      add_unwind_entry (output_bsp_when ());
-	      add_unwind_entry (output_bsp_gr (reg2));
-	      break;
-	    case REG_AR + AR_BSPSTORE:
-	      add_unwind_entry (output_bspstore_when ());
-	      add_unwind_entry (output_bspstore_gr (reg2));
-	      break;
-	    case REG_AR + AR_RNAT:
-	      add_unwind_entry (output_rnat_when ());
-	      add_unwind_entry (output_rnat_gr (reg2));
-	      break;
-	    case REG_AR + AR_UNAT:
-	      add_unwind_entry (output_unat_when ());
-	      add_unwind_entry (output_unat_gr (reg2));
-	      break;
-	    case REG_AR + AR_FPSR:
-	      add_unwind_entry (output_fpsr_when ());
-	      add_unwind_entry (output_fpsr_gr (reg2));
-	      break;
-	    case REG_AR + AR_PFS:
-	      add_unwind_entry (output_pfs_when ());
-	      if (! (unwind.prologue_mask & 4))
-		add_unwind_entry (output_pfs_gr (reg2));
-	      break;
-	    case REG_AR + AR_LC:
-	      add_unwind_entry (output_lc_when ());
-	      add_unwind_entry (output_lc_gr (reg2));
-	      break;
-	    case REG_BR:
-	      add_unwind_entry (output_rp_when ());
-	      if (! (unwind.prologue_mask & 8))
-		add_unwind_entry (output_rp_gr (reg2));
-	      break;
-	    case REG_PR:
-	      add_unwind_entry (output_preds_when ());
-	      if (! (unwind.prologue_mask & 1))
-		add_unwind_entry (output_preds_gr (reg2));
-	      break;
-	    case REG_PRIUNAT:
-	      add_unwind_entry (output_priunat_when_gr ());
-	      add_unwind_entry (output_priunat_gr (reg2));
-	      break;
-	    default:
-	      as_bad ("First operand not a valid register");
-	    }
-	}
-      else
-	as_bad (" Second operand not a valid register");
+      as_bad ("First operand to .save not a register");
+      reg1 = REG_PR; /* Anything valid is good here.  */
+    }
+  reg2 = e2.X_add_number - REG_GR;
+  if (e2.X_op != O_register || reg2 > 127)
+    {
+      as_bad ("Second operand to .save not a valid register");
+      reg2 = 0;
+    }
+  switch (reg1)
+    {
+    case REG_AR + AR_BSP:
+      add_unwind_entry (output_bsp_when (), sep);
+      add_unwind_entry (output_bsp_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_BSPSTORE:
+      add_unwind_entry (output_bspstore_when (), sep);
+      add_unwind_entry (output_bspstore_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_RNAT:
+      add_unwind_entry (output_rnat_when (), sep);
+      add_unwind_entry (output_rnat_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_UNAT:
+      add_unwind_entry (output_unat_when (), sep);
+      add_unwind_entry (output_unat_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_FPSR:
+      add_unwind_entry (output_fpsr_when (), sep);
+      add_unwind_entry (output_fpsr_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_PFS:
+      add_unwind_entry (output_pfs_when (), sep);
+      if (! (unwind.prologue_mask & 4))
+	add_unwind_entry (output_pfs_gr (reg2), NOT_A_CHAR);
+      else if (reg2 != unwind.prologue_gr
+		       + (unsigned) popcount (unwind.prologue_mask & (-4 << 1)))
+	as_warn ("Second operand of .save contradicts .prologue");
+      break;
+    case REG_AR + AR_LC:
+      add_unwind_entry (output_lc_when (), sep);
+      add_unwind_entry (output_lc_gr (reg2), NOT_A_CHAR);
+      break;
+    case REG_BR:
+      add_unwind_entry (output_rp_when (), sep);
+      if (! (unwind.prologue_mask & 8))
+	add_unwind_entry (output_rp_gr (reg2), NOT_A_CHAR);
+      else if (reg2 != unwind.prologue_gr)
+	as_warn ("Second operand of .save contradicts .prologue");
+      break;
+    case REG_PR:
+      add_unwind_entry (output_preds_when (), sep);
+      if (! (unwind.prologue_mask & 1))
+	add_unwind_entry (output_preds_gr (reg2), NOT_A_CHAR);
+      else if (reg2 != unwind.prologue_gr
+		       + (unsigned) popcount (unwind.prologue_mask & (-1 << 1)))
+	as_warn ("Second operand of .save contradicts .prologue");
+      break;
+    case REG_PRIUNAT:
+      add_unwind_entry (output_priunat_when_gr (), sep);
+      add_unwind_entry (output_priunat_gr (reg2), NOT_A_CHAR);
+      break;
+    default:
+      as_bad ("First operand to .save not a valid register");
+      add_unwind_entry (NULL, sep);
+      break;
     }
-  else
-    as_bad ("First operand not a register");
 }
 
 static void
 dot_restore (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
+  expressionS e1;
   unsigned long ecount;	/* # of _additional_ regions to pop */
   int sep;
 
   if (!in_body ("restore"))
     return;
 
-  sep = parse_operand (&e1);
+  sep = parse_operand (&e1, ',');
   if (e1.X_op != O_register || e1.X_add_number != REG_GR + 12)
-    {
-      as_bad ("First operand to .restore must be stack pointer (sp)");
-      return;
-    }
+    as_bad ("First operand to .restore must be stack pointer (sp)");
 
   if (sep == ',')
     {
-      parse_operand (&e2);
+      expressionS e2;
+
+      sep = parse_operand (&e2, ',');
       if (e2.X_op != O_constant || e2.X_add_number < 0)
 	{
 	  as_bad ("Second operand to .restore must be a constant >= 0");
-	  return;
+	  e2.X_add_number = 0;
 	}
       ecount = e2.X_add_number;
     }
@@ -3416,10 +3449,10 @@ dot_restore (dummy)
     {
       as_bad ("Epilogue count of %lu exceeds number of nested prologues (%u)",
 	      ecount + 1, unwind.prologue_count);
-      return;
+      ecount = 0;
     }
 
-  add_unwind_entry (output_epilogue (ecount));
+  add_unwind_entry (output_epilogue (ecount), sep);
 
   if (ecount < unwind.prologue_count)
     unwind.prologue_count -= ecount + 1;
@@ -3428,58 +3461,27 @@ dot_restore (dummy)
 }
 
 static void
-dot_restorereg (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  unsigned int ab, reg;
-  expressionS e;
-
-  if (!in_procedure ("restorereg"))
-    return;
-
-  parse_operand (&e);
-
-  if (!convert_expr_to_ab_reg (&e, &ab, &reg))
-    {
-      as_bad ("First operand to .restorereg must be a preserved register");
-      return;
-    }
-  add_unwind_entry (output_spill_reg (ab, reg, 0, 0));
-}
-
-static void
-dot_restorereg_p (dummy)
-     int dummy ATTRIBUTE_UNUSED;
+dot_restorereg (pred)
+     int pred;
 {
   unsigned int qp, ab, reg;
-  expressionS e1, e2;
+  expressionS e;
   int sep;
+  const char * const po = pred ? "restorereg.p" : "restorereg";
 
-  if (!in_procedure ("restorereg.p"))
+  if (!in_procedure (po))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("No second operand to .restorereg.p");
-      return;
-    }
-
-  parse_operand (&e2);
-
-  qp = e1.X_add_number - REG_P;
-  if (e1.X_op != O_register || qp > 63)
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
+  else
     {
-      as_bad ("First operand to .restorereg.p must be a predicate");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
-    {
-      as_bad ("Second operand to .restorereg.p must be a preserved register");
-      return;
-    }
-  add_unwind_entry (output_spill_reg_p (ab, reg, 0, 0, qp));
+  add_unwind_entry (output_spill_reg (ab, reg, 0, 0, qp), sep);
 }
 
 static char *special_linkonce_name[] =
@@ -3607,7 +3609,7 @@ generate_unwind_image (const segT text_s
 
   /* Mark the end of the unwind info, so that we can compute the size of the
      last unwind region.  */
-  add_unwind_entry (output_endp ());
+  add_unwind_entry (output_endp (), NOT_A_CHAR);
 
   /* Force out pending instructions, to make sure all unwind records have
      a valid slot_number field.  */
@@ -3723,12 +3725,14 @@ dot_altrp (dummy)
   if (!in_prologue ("altrp"))
     return;
 
-  parse_operand (&e);
+  parse_operand (&e, 0);
   reg = e.X_add_number - REG_BR;
-  if (e.X_op == O_register && reg < 8)
-    add_unwind_entry (output_rp_br (reg));
-  else
-    as_bad ("First operand not a valid branch register");
+  if (e.X_op != O_register || reg > 7)
+    {
+      as_bad ("First operand to .altrp not a valid branch register");
+      reg = 0;
+    }
+  add_unwind_entry (output_rp_br (reg), 0);
 }
 
 static void
@@ -3738,182 +3742,210 @@ dot_savemem (psprel)
   expressionS e1, e2;
   int sep;
   int reg1, val;
+  const char * const po = psprel ? "savepsp" : "savesp";
 
-  if (!in_prologue (psprel ? "savepsp" : "savesp"))
+  if (!in_prologue (po))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    as_bad ("No second operand to .save%ssp", psprel ? "p" : "");
-  sep = parse_operand (&e2);
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    sep = parse_operand (&e2, ',');
+  else
+    e2.X_op = O_absent;
 
   reg1 = e1.X_add_number;
   val = e2.X_add_number;
 
   /* Make sure its a valid ar.xxx reg, OR its br0, aka 'rp'.  */
-  if (e1.X_op == O_register)
+  if (e1.X_op != O_register)
     {
-      if (e2.X_op == O_constant)
-	{
-	  switch (reg1)
-	    {
-	    case REG_AR + AR_BSP:
-	      add_unwind_entry (output_bsp_when ());
-	      add_unwind_entry ((psprel
-				 ? output_bsp_psprel
-				 : output_bsp_sprel) (val));
-	      break;
-	    case REG_AR + AR_BSPSTORE:
-	      add_unwind_entry (output_bspstore_when ());
-	      add_unwind_entry ((psprel
-				 ? output_bspstore_psprel
-				 : output_bspstore_sprel) (val));
-	      break;
-	    case REG_AR + AR_RNAT:
-	      add_unwind_entry (output_rnat_when ());
-	      add_unwind_entry ((psprel
-				 ? output_rnat_psprel
-				 : output_rnat_sprel) (val));
-	      break;
-	    case REG_AR + AR_UNAT:
-	      add_unwind_entry (output_unat_when ());
-	      add_unwind_entry ((psprel
-				 ? output_unat_psprel
-				 : output_unat_sprel) (val));
-	      break;
-	    case REG_AR + AR_FPSR:
-	      add_unwind_entry (output_fpsr_when ());
-	      add_unwind_entry ((psprel
-				 ? output_fpsr_psprel
-				 : output_fpsr_sprel) (val));
-	      break;
-	    case REG_AR + AR_PFS:
-	      add_unwind_entry (output_pfs_when ());
-	      add_unwind_entry ((psprel
-				 ? output_pfs_psprel
-				 : output_pfs_sprel) (val));
-	      break;
-	    case REG_AR + AR_LC:
-	      add_unwind_entry (output_lc_when ());
-	      add_unwind_entry ((psprel
-				 ? output_lc_psprel
-				 : output_lc_sprel) (val));
-	      break;
-	    case REG_BR:
-	      add_unwind_entry (output_rp_when ());
-	      add_unwind_entry ((psprel
-				 ? output_rp_psprel
-				 : output_rp_sprel) (val));
-	      break;
-	    case REG_PR:
-	      add_unwind_entry (output_preds_when ());
-	      add_unwind_entry ((psprel
-				 ? output_preds_psprel
-				 : output_preds_sprel) (val));
-	      break;
-	    case REG_PRIUNAT:
-	      add_unwind_entry (output_priunat_when_mem ());
-	      add_unwind_entry ((psprel
-				 ? output_priunat_psprel
-				 : output_priunat_sprel) (val));
-	      break;
-	    default:
-	      as_bad ("First operand not a valid register");
-	    }
-	}
-      else
-	as_bad (" Second operand not a valid constant");
+      as_bad ("First operand to .%s not a register", po);
+      reg1 = REG_PR; /* Anything valid is good here.  */
+    }
+  if (e2.X_op != O_constant)
+    {
+      as_bad ("Second operand to .%s not a constant", po);
+      val = 0;
+    }
+
+  switch (reg1)
+    {
+    case REG_AR + AR_BSP:
+      add_unwind_entry (output_bsp_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_bsp_psprel
+			 : output_bsp_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_BSPSTORE:
+      add_unwind_entry (output_bspstore_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_bspstore_psprel
+			 : output_bspstore_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_RNAT:
+      add_unwind_entry (output_rnat_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_rnat_psprel
+			 : output_rnat_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_UNAT:
+      add_unwind_entry (output_unat_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_unat_psprel
+			 : output_unat_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_FPSR:
+      add_unwind_entry (output_fpsr_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_fpsr_psprel
+			 : output_fpsr_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_PFS:
+      add_unwind_entry (output_pfs_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_pfs_psprel
+			 : output_pfs_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_AR + AR_LC:
+      add_unwind_entry (output_lc_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_lc_psprel
+			 : output_lc_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_BR:
+      add_unwind_entry (output_rp_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_rp_psprel
+			 : output_rp_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_PR:
+      add_unwind_entry (output_preds_when (), sep);
+      add_unwind_entry ((psprel
+			 ? output_preds_psprel
+			 : output_preds_sprel) (val), NOT_A_CHAR);
+      break;
+    case REG_PRIUNAT:
+      add_unwind_entry (output_priunat_when_mem (), sep);
+      add_unwind_entry ((psprel
+			 ? output_priunat_psprel
+			 : output_priunat_sprel) (val), NOT_A_CHAR);
+      break;
+    default:
+      as_bad ("First operand to .%s not a valid register", po);
+      add_unwind_entry (NULL, sep);
+      break;
     }
-  else
-    as_bad ("First operand not a register");
 }
 
 static void
 dot_saveg (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
+  expressionS e;
+  unsigned grmask;
   int sep;
 
   if (!in_prologue ("save.g"))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep == ',')
-    parse_operand (&e2);
+  sep = parse_operand (&e, ',');
 
-  if (e1.X_op != O_constant)
-    as_bad ("First operand to .save.g must be a constant.");
-  else
+  grmask = e.X_add_number;
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0xf)
     {
-      int grmask = e1.X_add_number;
-      if (sep != ',')
-	add_unwind_entry (output_gr_mem (grmask));
-      else
+      as_bad ("First operand to .save.g must be a positive 4-bit constant");
+      grmask = 0;
+    }
+
+  if (sep == ',')
+    {
+      unsigned reg;
+      int n = popcount (grmask);
+
+      parse_operand (&e, 0);
+      reg = e.X_add_number - REG_GR;
+      if (e.X_op != O_register || reg > 127)
 	{
-	  int reg = e2.X_add_number - REG_GR;
-	  if (e2.X_op == O_register && reg >= 0 && reg < 128)
-	    add_unwind_entry (output_gr_gr (grmask, reg));
-	  else
-	    as_bad ("Second operand is an invalid register.");
+	  as_bad ("Second operand to .save.g must be a general register");
+	  reg = 0;
 	}
+      else if (reg > 128U - n)
+	{
+	  as_bad ("Second operand to .save.g must be the first of %d general registers", n);
+	  reg = 0;
+	}
+      add_unwind_entry (output_gr_gr (grmask, reg), 0);
     }
+  else
+    add_unwind_entry (output_gr_mem (grmask), 0);
 }
 
 static void
 dot_savef (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1;
-  int sep;
+  expressionS e;
 
   if (!in_prologue ("save.f"))
     return;
 
-  sep = parse_operand (&e1);
+  parse_operand (&e, 0);
 
-  if (e1.X_op != O_constant)
-    as_bad ("Operand to .save.f must be a constant.");
-  else
-    add_unwind_entry (output_fr_mem (e1.X_add_number));
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0xfffff)
+    {
+      as_bad ("Operand to .save.f must be a positive 20-bit constant");
+      e.X_add_number = 0;
+    }
+  add_unwind_entry (output_fr_mem (e.X_add_number), 0);
 }
 
 static void
 dot_saveb (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  expressionS e1, e2;
-  unsigned int reg;
-  unsigned char sep;
-  int brmask;
+  expressionS e;
+  unsigned brmask;
+  int sep;
 
   if (!in_prologue ("save.b"))
     return;
 
-  sep = parse_operand (&e1);
-  if (e1.X_op != O_constant)
+  sep = parse_operand (&e, ',');
+
+  brmask = e.X_add_number;
+  if (e.X_op != O_constant
+      || e.X_add_number <= 0
+      || e.X_add_number > 0x1f)
     {
-      as_bad ("First operand to .save.b must be a constant.");
-      return;
+      as_bad ("First operand to .save.b must be a positive 5-bit constant");
+      brmask = 0;
     }
-  brmask = e1.X_add_number;
 
   if (sep == ',')
     {
-      sep = parse_operand (&e2);
-      reg = e2.X_add_number - REG_GR;
-      if (e2.X_op != O_register || reg > 127)
+      unsigned reg;
+      int n = popcount (brmask);
+
+      parse_operand (&e, 0);
+      reg = e.X_add_number - REG_GR;
+      if (e.X_op != O_register || reg > 127)
 	{
-	  as_bad ("Second operand to .save.b must be a general register.");
-	  return;
+	  as_bad ("Second operand to .save.b must be a general register");
+	  reg = 0;
+	}
+      else if (reg > 128U - n)
+	{
+	  as_bad ("Second operand to .save.b must be the first of %d general registers", n);
+	  reg = 0;
 	}
-      add_unwind_entry (output_br_gr (brmask, e2.X_add_number));
+      add_unwind_entry (output_br_gr (brmask, reg), 0);
     }
   else
-    add_unwind_entry (output_br_mem (brmask));
-
-  if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    demand_empty_rest_of_line ();
+    add_unwind_entry (output_br_mem (brmask), 0);
 }
 
 static void
@@ -3921,23 +3953,38 @@ dot_savegf (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
-  int sep;
 
   if (!in_prologue ("save.gf"))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep == ',')
-    parse_operand (&e2);
-
-  if (e1.X_op != O_constant || sep != ',' || e2.X_op != O_constant)
-    as_bad ("Both operands of .save.gf must be constants.");
+  if (parse_operand (&e1, ',') == ',')
+    parse_operand (&e2, 0);
   else
-    {
-      int grmask = e1.X_add_number;
-      int frmask = e2.X_add_number;
-      add_unwind_entry (output_frgr_mem (grmask, frmask));
-    }
+    e2.X_op = O_absent;
+
+  if (e1.X_op != O_constant
+      || e1.X_add_number < 0
+      || e1.X_add_number > 0xf)
+    {
+      as_bad ("First operand to .save.gf must be a non-negative 4-bit constant");
+      e1.X_op = O_absent;
+      e1.X_add_number = 0;
+    }
+  if (e2.X_op != O_constant
+      || e2.X_add_number < 0
+      || e2.X_add_number > 0xfffff)
+    {
+      as_bad ("Second operand to .save.gf must be a non-negative 20-bit constant");
+      e2.X_op = O_absent;
+      e2.X_add_number = 0;
+    }
+  if (e1.X_op == O_constant
+      && e2.X_op == O_constant
+      && e1.X_add_number == 0
+      && e2.X_add_number == 0)
+    as_bad ("Operands to .save.gf may not be both zero");
+
+  add_unwind_entry (output_frgr_mem (e1.X_add_number, e2.X_add_number), 0);
 }
 
 static void
@@ -3945,201 +3992,93 @@ dot_spill (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
   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 ())
-    demand_empty_rest_of_line ();
+  parse_operand (&e, 0);
 
   if (e.X_op != O_constant)
-    as_bad ("Operand to .spill must be a constant");
-  else
-    add_unwind_entry (output_spill_base (e.X_add_number));
-}
-
-static void
-dot_spillreg (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  int sep;
-  unsigned int ab, xy, reg, treg;
-  expressionS e1, e2;
-
-  if (!in_procedure ("spillreg"))
-    return;
-
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("No second operand to .spillreg");
-      return;
-    }
-
-  parse_operand (&e2);
-
-  if (!convert_expr_to_ab_reg (&e1, &ab, &reg))
-    {
-      as_bad ("First operand to .spillreg must be a preserved register");
-      return;
-    }
-
-  if (!convert_expr_to_xy_reg (&e2, &xy, &treg))
     {
-      as_bad ("Second operand to .spillreg must be a register");
-      return;
+      as_bad ("Operand to .spill must be a constant");
+      e.X_add_number = 0;
     }
-
-  add_unwind_entry (output_spill_reg (ab, reg, treg, xy));
+  add_unwind_entry (output_spill_base (e.X_add_number), 0);
 }
 
 static void
-dot_spillmem (psprel)
-     int psprel;
+dot_spillreg (pred)
+     int pred;
 {
-  expressionS e1, e2;
   int sep;
-  unsigned int ab, reg;
+  unsigned int qp, ab, xy, reg, treg;
+  expressionS e;
+  const char * const po = pred ? "spillreg.p" : "spillreg";
 
-  if (!in_procedure ("spillmem"))
+  if (!in_procedure (po))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand missing");
-      return;
-    }
-
-  parse_operand (&e2);
-
-  if (!convert_expr_to_ab_reg (&e1, &ab, &reg))
-    {
-      as_bad ("First operand to .spill%s must be a preserved register",
-	      psprel ? "psp" : "sp");
-      return;
-    }
-
-  if (e2.X_op != O_constant)
-    {
-      as_bad ("Second operand to .spill%s must be a constant",
-	      psprel ? "psp" : "sp");
-      return;
-    }
-
-  if (psprel)
-    add_unwind_entry (output_spill_psprel (ab, reg, e2.X_add_number));
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
   else
-    add_unwind_entry (output_spill_sprel (ab, reg, e2.X_add_number));
-}
-
-static void
-dot_spillreg_p (dummy)
-     int dummy ATTRIBUTE_UNUSED;
-{
-  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 != ',')
-    {
-      as_bad ("No second and third operand to .spillreg.p");
-      return;
-    }
-
-  sep = parse_operand (&e2);
-  if (sep != ',')
-    {
-      as_bad ("No third operand to .spillreg.p");
-      return;
-    }
-
-  parse_operand (&e3);
-
-  qp = e1.X_add_number - REG_P;
-
-  if (e1.X_op != O_register || qp > 63)
     {
-      as_bad ("First operand to .spillreg.p must be a predicate");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
-    {
-      as_bad ("Second operand to .spillreg.p must be a preserved register");
-      return;
-    }
-
-  if (!convert_expr_to_xy_reg (&e3, &xy, &treg))
-    {
-      as_bad ("Third operand to .spillreg.p must be a register");
-      return;
-    }
+  if (sep == ',')
+    sep = parse_operand (&e, ',');
+  else
+    e.X_op = O_absent;
+  convert_expr_to_xy_reg (&e, &xy, &treg, po, 2 + pred);
 
-  add_unwind_entry (output_spill_reg_p (ab, reg, treg, xy, qp));
+  add_unwind_entry (output_spill_reg (ab, reg, treg, xy, qp), sep);
 }
 
 static void
-dot_spillmem_p (psprel)
+dot_spillmem (psprel)
      int psprel;
 {
-  expressionS e1, e2, e3;
-  int sep;
-  unsigned int ab, reg;
-  unsigned int qp;
-
-  if (!in_procedure ("spillmem.p"))
-    return;
-
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand missing");
-      return;
-    }
+  expressionS e;
+  int pred = (psprel < 0), sep;
+  unsigned int qp, ab, reg;
+  const char * po;
 
-  parse_operand (&e2);
-  if (sep != ',')
+  if (pred)
     {
-      as_bad ("Second operand missing");
-      return;
+      psprel = ~psprel;
+      po = psprel ? "spillpsp.p" : "spillsp.p";
     }
+  else
+    po = psprel ? "spillpsp" : "spillsp";
 
-  parse_operand (&e3);
-
-  qp = e1.X_add_number - REG_P;
-  if (e1.X_op != O_register || qp > 63)
-    {
-      as_bad ("First operand to .spill%s_p must be a predicate",
-	      psprel ? "psp" : "sp");
-      return;
-    }
+  if (!in_procedure (po))
+    return;
 
-  if (!convert_expr_to_ab_reg (&e2, &ab, &reg))
+  if (pred)
+    sep = parse_predicate_and_operand (&e, &qp, po);
+  else
     {
-      as_bad ("Second operand to .spill%s_p must be a preserved register",
-	      psprel ? "psp" : "sp");
-      return;
+      sep = parse_operand (&e, ',');
+      qp = 0;
     }
+  convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
 
-  if (e3.X_op != O_constant)
+  if (sep == ',')
+    sep = parse_operand (&e, ',');
+  else
+    e.X_op = O_absent;
+  if (e.X_op != O_constant)
     {
-      as_bad ("Third operand to .spill%s_p must be a constant",
-	      psprel ? "psp" : "sp");
-      return;
+      as_bad ("Operand %d to .%s must be a constant", 2 + pred, po);
+      e.X_add_number = 0;
     }
 
   if (psprel)
-    add_unwind_entry (output_spill_psprel_p (ab, reg, e3.X_add_number, qp));
+    add_unwind_entry (output_spill_psprel (ab, reg, e.X_add_number, qp), sep);
   else
-    add_unwind_entry (output_spill_sprel_p (ab, reg, e3.X_add_number, qp));
+    add_unwind_entry (output_spill_sprel (ab, reg, e.X_add_number, qp), sep);
 }
 
 static unsigned int
@@ -4206,14 +4145,15 @@ dot_label_state (dummy)
   if (!in_body ("label_state"))
     return;
 
-  parse_operand (&e);
-  if (e.X_op != O_constant)
+  parse_operand (&e, 0);
+  if (e.X_op == O_constant)
+    save_prologue_count (e.X_add_number, unwind.prologue_count);
+  else
     {
       as_bad ("Operand to .label_state must be a constant");
-      return;
+      e.X_add_number = 0;
     }
-  add_unwind_entry (output_label_state (e.X_add_number));
-  save_prologue_count (e.X_add_number, unwind.prologue_count);
+  add_unwind_entry (output_label_state (e.X_add_number), 0);
 }
 
 static void
@@ -4225,14 +4165,15 @@ dot_copy_state (dummy)
   if (!in_body ("copy_state"))
     return;
 
-  parse_operand (&e);
-  if (e.X_op != O_constant)
+  parse_operand (&e, 0);
+  if (e.X_op == O_constant)
+    unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
+  else
     {
       as_bad ("Operand to .copy_state must be a constant");
-      return;
+      e.X_add_number = 0;
     }
-  add_unwind_entry (output_copy_state (e.X_add_number));
-  unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
+  add_unwind_entry (output_copy_state (e.X_add_number), 0);
 }
 
 static void
@@ -4242,32 +4183,28 @@ dot_unwabi (dummy)
   expressionS e1, e2;
   unsigned char sep;
 
-  if (!in_procedure ("unwabi"))
+  if (!in_prologue ("unwabi"))
     return;
 
-  sep = parse_operand (&e1);
-  if (sep != ',')
-    {
-      as_bad ("Second operand to .unwabi missing");
-      return;
-    }
-  sep = parse_operand (&e2);
-  if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-    demand_empty_rest_of_line ();
+  sep = parse_operand (&e1, ',');
+  if (sep == ',')
+    parse_operand (&e2, 0);
+  else
+    e2.X_op = O_absent;
 
   if (e1.X_op != O_constant)
     {
       as_bad ("First operand to .unwabi must be a constant");
-      return;
+      e1.X_add_number = 0;
     }
 
   if (e2.X_op != O_constant)
     {
       as_bad ("Second operand to .unwabi must be a constant");
-      return;
+      e2.X_add_number = 0;
     }
 
-  add_unwind_entry (output_unwabi (e1.X_add_number, e2.X_add_number));
+  add_unwind_entry (output_unwabi (e1.X_add_number, e2.X_add_number), 0);
 }
 
 static void
@@ -4374,16 +4311,14 @@ dot_body (dummy)
   unwind.prologue_mask = 0;
   unwind.body = 1;
 
-  add_unwind_entry (output_body ());
-  demand_empty_rest_of_line ();
+  add_unwind_entry (output_body (), 0);
 }
 
 static void
 dot_prologue (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
-  unsigned char sep;
-  int mask = 0, grsave = 0;
+  unsigned mask = 0, grsave = 0;
 
   if (!in_procedure ("prologue"))
     return;
@@ -4398,36 +4333,53 @@ dot_prologue (dummy)
 
   if (!is_it_end_of_statement ())
     {
-      expressionS e1, e2;
-      sep = parse_operand (&e1);
-      if (sep != ',')
-	as_bad ("No second operand to .prologue");
-      sep = parse_operand (&e2);
-      if (!is_end_of_line[sep] && !is_it_end_of_statement ())
-	demand_empty_rest_of_line ();
-
-      if (e1.X_op == O_constant)
-	{
-	  mask = e1.X_add_number;
+      expressionS e;
+      int n, sep = parse_operand (&e, ',');
 
-	  if (e2.X_op == O_constant)
-	    grsave = e2.X_add_number;
-	  else if (e2.X_op == O_register
-		   && (grsave = e2.X_add_number - REG_GR) < 128)
-	    ;
-	  else
-	    as_bad ("Second operand not a constant or general register");
+      if (e.X_op != O_constant
+	  || e.X_add_number < 0
+	  || e.X_add_number > 0xf)
+	as_bad ("First operand to .prologue must be a positive 4-bit constant");
+      else if (e.X_add_number == 0)
+	as_warn ("Pointless use of zero first operand to .prologue");
+      else
+	mask = e.X_add_number;
+	n = popcount (mask);
 
-	  add_unwind_entry (output_prologue_gr (mask, grsave));
-	}
+      if (sep == ',')
+	parse_operand (&e, 0);
       else
-	as_bad ("First operand not a constant");
+	e.X_op = O_absent;
+      if (e.X_op == O_constant
+	  && e.X_add_number >= 0
+	  && e.X_add_number < 128)
+	{
+	  if (md.unwind_check == unwind_check_error)
+	    as_warn ("Using a constant as second operand to .prologue is deprecated");
+	  grsave = e.X_add_number;
+	}
+      else if (e.X_op != O_register
+	       || (grsave = e.X_add_number - REG_GR) > 127)
+	{
+	  as_bad ("Second operand to .prologue must be a general register");
+	  grsave = 0;
+	}
+      else if (grsave > 128U - n)
+	{
+	  as_bad ("Second operand to .prologue must be the first of %d general registers", n);
+	  grsave = 0;
+	}
+
     }
+
+  if (mask)
+    add_unwind_entry (output_prologue_gr (mask, grsave), 0);
   else
-    add_unwind_entry (output_prologue ());
+    add_unwind_entry (output_prologue (), 0);
 
   unwind.prologue = 1;
   unwind.prologue_mask = mask;
+  unwind.prologue_gr = grsave;
   unwind.body = 0;
   ++unwind.prologue_count;
 }
@@ -5332,11 +5284,11 @@ const pseudo_typeS md_pseudo_table[] =
     { "fframe", dot_fframe, 0 },
     { "vframe", dot_vframe, 0 },
     { "vframesp", dot_vframesp, 0 },
-    { "vframepsp", dot_vframepsp, 0 },
+    { "vframepsp", dot_vframesp, 1 },
     { "save", dot_save, 0 },
     { "restore", dot_restore, 0 },
     { "restorereg", dot_restorereg, 0 },
-    { "restorereg.p", dot_restorereg_p, 0 },
+    { "restorereg.p", dot_restorereg, 1 },
     { "handlerdata", dot_handlerdata, 0 },
     { "unwentry", dot_unwentry, 0 },
     { "altrp", dot_altrp, 0 },
@@ -5350,9 +5302,9 @@ const pseudo_typeS md_pseudo_table[] =
     { "spillreg", dot_spillreg, 0 },
     { "spillsp", dot_spillmem, 0 },
     { "spillpsp", dot_spillmem, 1 },
-    { "spillreg.p", dot_spillreg_p, 0 },
-    { "spillsp.p", dot_spillmem_p, 0 },
-    { "spillpsp.p", dot_spillmem_p, 1 },
+    { "spillreg.p", dot_spillreg, 1 },
+    { "spillsp.p", dot_spillmem, ~0 },
+    { "spillpsp.p", dot_spillmem, ~1 },
     { "label_state", dot_label_state, 0 },
     { "copy_state", dot_copy_state, 0 },
     { "unwabi", dot_unwabi, 0 },
@@ -6045,27 +5997,19 @@ operand_match (idesc, index, e)
 }
 
 static int
-parse_operand (e)
+parse_operand (e, more)
      expressionS *e;
+     int more;
 {
   int sep = '\0';
 
   memset (e, 0, sizeof (*e));
   e->X_op = O_absent;
   SKIP_WHITESPACE ();
-  if (*input_line_pointer != '}')
-    expression (e);
-  sep = *input_line_pointer++;
-
-  if (sep == '}')
-    {
-      if (!md.manual_bundling)
-	as_warn ("Found '}' when manual bundling is off");
-      else
-	CURR_SLOT.manual_bundling_off = 1;
-      md.manual_bundling = 0;
-      sep = '\0';
-    }
+  expression (e);
+  sep = *input_line_pointer;
+  if (more && (sep == ',' || sep == more))
+    ++input_line_pointer;
   return sep;
 }
 
@@ -6124,7 +6068,7 @@ parse_operands (idesc)
     {
       if (i < NELEMS (CURR_SLOT.opnd)) 
 	{
-	  sep = parse_operand (CURR_SLOT.opnd + i);
+	  sep = parse_operand (CURR_SLOT.opnd + i, '=');
 	  if (CURR_SLOT.opnd[i].X_op == O_absent)
 	    break;
 	}
@@ -6132,7 +6076,7 @@ parse_operands (idesc)
 	{
 	  expressionS dummy;
 
-	  sep = parse_operand (&dummy);
+	  sep = parse_operand (&dummy, '=');
 	  if (dummy.X_op == O_absent)
 	    break;
 	}
@@ -6177,7 +6121,7 @@ parse_operands (idesc)
 	  /* now we can parse the first arg:  */
 	  saved_input_pointer = input_line_pointer;
 	  input_line_pointer = first_arg;
-	  sep = parse_operand (CURR_SLOT.opnd + 0);
+	  sep = parse_operand (CURR_SLOT.opnd + 0, '=');
 	  if (sep != '=')
 	    --num_outputs;	/* force error */
 	  input_line_pointer = saved_input_pointer;
@@ -7614,6 +7558,15 @@ ia64_end_of_source ()
 void
 ia64_start_line ()
 {
+  static int first;
+
+  if (!first) {
+    /* Make sure we don't reference input_line_pointer[-1] when that's
+       not valid.  */
+    first = 1;
+    return;
+  }
+
   if (md.qp.X_op == O_register)
     as_bad ("qualifying predicate not followed by instruction");
   md.qp.X_op = O_absent;
@@ -7636,38 +7589,8 @@ ia64_start_line ()
       else
 	insn_group_break (1, 0, 0);
     }
-}
-
-/* This is a hook for ia64_frob_label, so that it can distinguish tags from
-   labels.  */
-static int defining_tag = 0;
-
-int
-ia64_unrecognized_line (ch)
-     int ch;
-{
-  switch (ch)
+  else if (input_line_pointer[-1] == '{')
     {
-    case '(':
-      expression (&md.qp);
-      if (*input_line_pointer++ != ')')
-	{
-	  as_bad ("Expected ')'");
-	  return 0;
-	}
-      if (md.qp.X_op != O_register)
-	{
-	  as_bad ("Qualifying predicate expected");
-	  return 0;
-	}
-      if (md.qp.X_add_number < REG_P || md.qp.X_add_number >= REG_P + 64)
-	{
-	  as_bad ("Predicate register expected");
-	  return 0;
-	}
-      return 1;
-
-    case '{':
       if (md.manual_bundling)
 	as_warn ("Found '{' when manual bundling is already turned on");
       else
@@ -7684,9 +7607,9 @@ ia64_unrecognized_line (ch)
 	  else
 	    as_warn (_("Found '{' after explicit switch to automatic mode"));
 	}
-      return 1;
-
-    case '}':
+    }
+  else if (input_line_pointer[-1] == '}')
+    {
       if (!md.manual_bundling)
 	as_warn ("Found '}' when manual bundling is off");
       else
@@ -7699,17 +7622,36 @@ ia64_unrecognized_line (ch)
 	  && !md.mode_explicitly_set
 	  && !md.default_explicit_mode)
 	dot_dv_mode ('A');
+    }
+}
 
-      /* Allow '{' to follow on the same line.  We also allow ";;", but that
-	 happens automatically because ';' is an end of line marker.  */
-      SKIP_WHITESPACE ();
-      if (input_line_pointer[0] == '{')
+/* This is a hook for ia64_frob_label, so that it can distinguish tags from
+   labels.  */
+static int defining_tag = 0;
+
+int
+ia64_unrecognized_line (ch)
+     int ch;
+{
+  switch (ch)
+    {
+    case '(':
+      expression (&md.qp);
+      if (*input_line_pointer++ != ')')
 	{
-	  input_line_pointer++;
-	  return ia64_unrecognized_line ('{');
+	  as_bad ("Expected ')'");
+	  return 0;
+	}
+      if (md.qp.X_op != O_register)
+	{
+	  as_bad ("Qualifying predicate expected");
+	  return 0;
+	}
+      if (md.qp.X_add_number < REG_P || md.qp.X_add_number >= REG_P + 64)
+	{
+	  as_bad ("Predicate register expected");
+	  return 0;
 	}
-
-      demand_empty_rest_of_line ();
       return 1;
 
     case '[':
@@ -11732,7 +11674,7 @@ dot_alias (int section)
   if (name == end_name)
     {
       as_bad (_("expected symbol name"));
-      discard_rest_of_line ();
+      ignore_rest_of_line ();
       return;
     }
 
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/group-2.s	2005-05-08 00:19:37.000000000 +0200
+++ 2005-06-27/gas/testsuite/gas/ia64/group-2.s	2005-05-20 16:53:54.000000000 +0200
@@ -1,6 +1,6 @@
 	.section	.gnu.linkonce.t.foo,"axG",@progbits,foo,comdat
 	.proc foo#
 foo:
-	.prologue 12, 33
+	.prologue 12, r33
 	;;
 	.endp foo#
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/ia64.exp	2005-05-25 08:57:45.000000000 +0200
+++ 2005-06-27/gas/testsuite/gas/ia64/ia64.exp	2005-06-16 17:01:14.000000000 +0200
@@ -84,7 +84,10 @@ if [istarget "ia64-*"] then {
     run_list_test "proc" "-munwind-check=error"
     run_list_test "radix" ""
     run_list_test "slot2" ""
+    run_dump_test "strange"
+    run_list_test "unwind-bad" ""
     run_list_test "unwind-err" "-munwind-check=error"
+    run_dump_test "unwind-ok"
     run_dump_test "operand-or"
     run_list_test "hint.b-err" ""
     run_list_test "hint.b-warn" "-mhint.b=warning"
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/strange.d	1970-01-01 01:00:00.000000000 +0100
+++ 2005-06-27/gas/testsuite/gas/ia64/strange.d	2005-06-10 17:05:50.000000000 +0200
@@ -0,0 +1,19 @@
+#objdump: -s
+#name: ia64 strange
+
+.*: +file format .*
+
+Contents of section .text:
+ 0000 0c000000 01001000 00020000 00000400  .*
+ 0010 04000000 01000000 00000020 00000400  .*
+ 0020 0c000000 01002000 00020000 00000400  .*
+ 0030 04000000 01000000 00000040 00000400  .*
+ 0040 1c000000 01003000 00020000 00000020  .*
+ 0050 04000000 01000000 00000080 00000400  .*
+ 0060 04000000 01000000 000000a0 00000400  .*
+ 0070 04000000 01000000 000000c0 00000400  .*
+ 0080 04000000 01000000 000000e0 00000400  .*
+ 0090 0e000000 01000000 00020000 01000400  .*
+ 00a0 1d000000 01009000 00020080 00008400  .*
+Contents of section .data:
+ 0000 ffffff                               .*
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/strange.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-06-27/gas/testsuite/gas/ia64/strange.s	2005-06-10 17:01:34.000000000 +0200
@@ -0,0 +1,18 @@
+.explicit
+.text
+_start:
+{.mfi
+	nop.f		1 } nop.x 1
+{.mfi
+	nop.f		2
+}	nop.x		2
+{.mfb
+	nop.f		3
+.xdata1 .data, -1 } .xdata1 .data, -1
+	nop.x		4 { nop.x 5
+} {	nop.x		6 }
+	nop.x		7 {.mmf
+	nop.f		8
+} .xdata1 .data, -1 { .mfb
+	nop.f		9
+	br.ret.sptk	rp }
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/unwind-bad.l	1970-01-01 01:00:00.000000000 +0100
+++ 2005-06-27/gas/testsuite/gas/ia64/unwind-bad.l	2005-05-20 16:34:10.000000000 +0200
@@ -0,0 +1,51 @@
+.*: Assembler messages:
+.*:8: Error: First operand to \.save\.g must be a positive 4-bit constant
+.*:10: Error: First operand to \.save\.g must be a positive 4-bit constant
+.*:12: Error: First operand to \.save\.g must be a positive 4-bit constant
+#FIXME .*:16: Error: Previous spill incomplete
+#FIXME .*:18: Error: Register r4 was already saved
+.*:20: Error: Operand to \.save\.f must be a positive 20-bit constant
+.*:22: Error: Operand to \.save\.f must be a positive 20-bit constant
+.*:24: Error: Operand to \.save\.f must be a positive 20-bit constant
+#FIXME .*:28: Error: Previous spill incomplete
+#FIXME .*:30: Error: Register f2 was already saved
+.*:32: Error: First operand to \.save\.b must be a positive 5-bit constant
+.*:34: Error: First operand to \.save\.b must be a positive 5-bit constant
+.*:36: Error: First operand to \.save\.b must be a positive 5-bit constant
+#FIXME .*:40: Error: Previous spill incomplete
+#FIXME .*:42: Error: Register b1 was already saved
+.*:44: Error: Operand 2 to \.spillreg must be a writable register
+.*:46: Error: Operand 1 to \.spillreg must be a preserved register
+.*:48: Error: Operand 1 to \.spillreg must be a preserved register
+.*:50: Error: Operand 1 to \.spillreg must be a preserved register
+.*:52: Error: Operand 2 to \.spillreg must be a writable register
+.*:54: Error: Operand 2 to \.spillreg must be a writable register
+.*:56: Error: Operand 1 to \.spillreg must be a preserved register
+#FIXME .*:58: Error: Floating point register cannot be spilled to general register
+#FIXME .*:60: Error: Floating point register cannot be spilled to branch register
+.*:62: Warning: Pointless use of p0 as first operand to \.spillreg\.p
+.*:64: Error: Operand 3 to \.spillreg.p must be a writable register
+.*:66: Error: Operand 3 to \.spillreg.p must be a writable register
+.*:68: Warning: Pointless use of p0 as first operand to \.restorereg\.p
+.*:78: Error: Operands to \.save\.gf may not be both zero
+.*:80: Error: First operand to \.save\.gf must be a non-negative 4-bit constant
+.*:82: Error: Second operand to \.save\.gf must be a non-negative 20-bit constant
+.*:84: Error: First operand to \.save\.gf must be a non-negative 4-bit constant
+.*:86: Error: Second operand to \.save\.gf must be a non-negative 20-bit constant
+#FIXME .*:90: Error: Previous spill incomplete
+#FIXME .*:92: Error: Register r4 was already saved
+#FIXME .*:94: Error: Register f2 was already saved
+.*:98: Error: Epilogue count of 2 exceeds number of nested prologues \(1\)
+.*:100: Error: Missing \.label_state 2
+.*:108: Error: First operand to \.save\.g must be a positive 4-bit constant
+#FIXME .*:110: Error: Second operand to \.save\.g must be a writable general registers
+.*:112: Error: Second operand to \.save\.g must be the first of 2 general registers
+.*:115: Error: First operand to \.save\.b must be a positive 5-bit constant
+#FIXME .*:117: Error: Second operand to \.save\.b must be a writable general registers
+.*:119: Error: Second operand to \.save\.b must be the first of 2 general registers
+.*:128: Error: First operand to \.prologue must be a positive 4-bit constant
+.*:134: Warning: Pointless use of zero first operand to \.prologue
+.*:140: Error: First operand to \.prologue must be a positive 4-bit constant
+#FIXME .*:141: Error: Operand to \.vframe must be a writable general registers
+#FIXME .*:147: Error: Second operand to \.prologue must be a writable general registers
+.*:153: Error: Second operand to \.prologue must be the first of 2 general registers
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/unwind-bad.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-06-27/gas/testsuite/gas/ia64/unwind-bad.s	2005-05-20 16:23:42.000000000 +0200
@@ -0,0 +1,155 @@
+.text
+
+.proc full1
+full1:
+
+.prologue
+.spill 0
+.save.g 0
+	nop		0
+.save.g 0x10
+	nop		0
+.save.g -1
+	nop		0
+.save.g 0x3
+	nop		0
+.save.g 0x4
+	nop		0
+.save.g 0x1
+	nop		0
+.save.f 0
+	nop		0
+.save.f 0x100000
+	nop		0
+.save.f -1
+	nop		0
+.save.f 0x3
+	nop		0
+.save.f 0x4
+	nop		0
+.save.f 0x1
+	nop		0
+.save.b 0
+	nop		0
+.save.b 0x20
+	nop		0
+.save.b -1
+	nop		0
+.save.b 0x3
+	nop		0
+.save.b 0x4
+	nop		0
+.save.b 0x1
+	nop		0
+.spillreg r4, r0
+	nop		0
+.spillreg r3, r2
+	nop		0
+.spillreg r8, r9
+	nop		0
+.spillreg b6, r10
+	nop		0
+.spillreg f2, f0
+	nop		0
+.spillreg f3, f1
+	nop		0
+.spillreg f6, f7
+	nop		0
+.spillreg f4, r11
+	nop		0
+.spillreg f5, b0
+	nop		0
+.spillreg.p p0, r4, r3
+	nop		0
+.spillreg.p p1, r4, r0
+	nop		0
+.spillreg.p p1, f16, f0
+	nop		0
+.restorereg.p p0, r4
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp full1
+
+.proc full2
+full2:
+.prologue
+.spill 0
+.save.gf 0, 0
+	nop		0
+.save.gf 0x10, 0
+	nop		0
+.save.gf 0, 0x100000
+	nop		0
+.save.gf ~0, 0
+	nop		0
+.save.gf 0, ~0
+	nop		0
+.save.gf 1, 1
+	nop		0
+.save.gf 2, 0
+	nop		0
+.save.gf 1, 0
+	nop		0
+.save.gf 0, 1
+	nop		0
+.body
+.label_state 1
+.restore sp, 1
+	nop.x		0
+.copy_state 2
+	br.ret.sptk	rp
+.endp full2
+
+.proc full3
+full3:
+.prologue
+.spill 0
+.save.g 0x10, r16
+	nop		0
+.save.g 0x01, r0
+	nop		0
+.save.g 0x06, r127
+	nop		0
+	nop		0
+.save.b 0x20, r16
+	nop		0
+.save.b 0x01, r0
+	nop		0
+.save.b 0x18, r127
+	nop		0
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp full3
+
+.proc simple1
+simple1:
+.prologue 0x10, r2
+	br.ret.sptk	rp
+.endp simple1
+
+.proc simple2
+simple2:
+.prologue 0, r2
+	br.ret.sptk	rp
+.endp simple2
+
+.proc simple3
+simple3:
+.prologue -1, r2
+.vframe r0
+	br.ret.sptk	rp
+.endp simple3
+
+.proc simple4
+simple4:
+.prologue 0x1, r0
+	br.ret.sptk	rp
+.endp simple4
+
+.proc simple5
+simple5:
+.prologue 0xc, r127
+	br.ret.sptk	rp
+.endp simple5
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/unwind-err.l	2005-02-08 10:38:38.000000000 +0100
+++ 2005-06-27/gas/testsuite/gas/ia64/unwind-err.l	2005-06-27 11:38:22.044871943 +0200
@@ -8,27 +8,28 @@
 .*:7: Error: .body outside of procedure
 .*:8: Error: .spillreg outside of procedure
 .*:9: Error: .spillreg.p outside of procedure
-.*:10: Error: .spillmem outside of procedure
-.*:11: Error: .spillmem.p outside of procedure
-.*:12: Error: .spillmem outside of procedure
-.*:13: Error: .spillmem.p outside of procedure
+.*:10: Error: .spillsp outside of procedure
+.*:11: Error: .spillsp.p outside of procedure
+.*:12: Error: .spillpsp outside of procedure
+.*:13: Error: .spillpsp.p outside of procedure
 .*:14: Error: .restorereg outside of procedure
 .*:15: Error: .restorereg.p outside of procedure
 .*:24: Error: .label_state outside of body region
 .*:25: Error: .copy_state outside of body region
 .*:26: Error: .fframe outside of prologue
 .*:27: Error: .vframe outside of prologue
-.*:28: Error: .spill outside of prologue
-.*:29: Error: .restore outside of body region
-.*:30: Error: .save outside of prologue
-.*:31: Error: .savesp outside of prologue
-.*:32: Error: .savepsp outside of prologue
-.*:33: Error: .save.g outside of prologue
-.*:34: Error: .save.gf outside of prologue
-.*:35: Error: .save.f outside of prologue
-.*:36: Error: .save.b outside of prologue
-.*:37: Error: .altrp outside of prologue
-.*:42: Error: .prologue within prologue
-.*:50: Error: .body outside of procedure
-.*:57: Warning: Initial .prologue.*
-.*:64: Warning: Initial .body.*
+.*:28: Error: .vframesp outside of prologue
+.*:29: Error: .spill outside of prologue
+.*:30: Error: .restore outside of body region
+.*:31: Error: .save outside of prologue
+.*:32: Error: .savesp outside of prologue
+.*:33: Error: .savepsp outside of prologue
+.*:34: Error: .save.g outside of prologue
+.*:35: Error: .save.gf outside of prologue
+.*:36: Error: .save.f outside of prologue
+.*:37: Error: .save.b outside of prologue
+.*:38: Error: .altrp outside of prologue
+.*:43: Error: .prologue within prologue
+.*:51: Error: .body outside of procedure
+.*:58: Warning: Initial .prologue.*
+.*:65: Warning: Initial .body.*
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/unwind-err.s	2005-02-08 10:36:18.000000000 +0100
+++ 2005-06-27/gas/testsuite/gas/ia64/unwind-err.s	2005-06-27 11:37:52.353466057 +0200
@@ -25,6 +25,7 @@ start:
 .copy_state 1
 .fframe 0
 .vframe r0
+.vframesp 0
 .spill 0
 .restore sp
 .save rp, r0
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/unwind-ok.d	1970-01-01 01:00:00.000000000 +0100
+++ 2005-06-27/gas/testsuite/gas/ia64/unwind-ok.d	2005-06-27 11:37:31.680614748 +0200
@@ -0,0 +1,224 @@
+#readelf: -u
+#name: ia64 unwind descriptors
+
+Unwind section '\.IA_64\.unwind' at offset 0x[[:xdigit:]]+ contains 8 entries:
+
+<full1>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x3 \( ?ehandler uhandler\), len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=8\)
+[[:space:]]*P6:fr_mem\(frmask=\[f2,f5\]\)
+[[:space:]]*P6:gr_mem\(grmask=\[r4,r7\]\)
+[[:space:]]*P1:br_mem\(brmask=\[b1,b5\]\)
+[[:space:]]*P4:spill_mask\(imask=\[rfb,rfb,--\]\)
+[[:space:]]*P7:spill_base\(pspoff=0x10-0x10\)
+[[:space:]]*P3:rp_br\(reg=b7\)
+[[:space:]]*P10:unwabi\(abi=@svr4,context=0x00\)
+[[:space:]]*R1:body\(rlen=[[:digit:]]+\)
+[[:space:]]*X2:spill_reg\(t=0,reg=r4,treg=r2\)
+[[:space:]]*X4:spill_reg_p\(qp=p1,t=1,reg=r7,treg=r31\)
+[[:space:]]*X1:spill_sprel\(reg=b1,t=2,spoff=0x8\)
+[[:space:]]*X3:spill_sprel_p\(qp=p2,t=3,reg=b5,spoff=0x10\)
+[[:space:]]*X1:spill_psprel\(reg=f2,t=4,pspoff=0x10-0x28\)
+[[:space:]]*X3:spill_psprel_p\(qp=p4,t=5,reg=f5,pspoff=0x10-0x30\)
+[[:space:]]*X2:restore\(t=6,reg=f16\)
+[[:space:]]*X4:restore_p\(qp=p8,t=7,reg=f31\)
+[[:space:]]*X2:spill_reg\(t=8,reg=ar\.bsp,treg=r16\)
+[[:space:]]*X2:spill_reg\(t=9,reg=ar\.bspstore,treg=r17\)
+[[:space:]]*X2:spill_reg\(t=10,reg=ar\.fpsr,treg=r18\)
+[[:space:]]*X2:spill_reg\(t=11,reg=ar\.lc,treg=r19\)
+[[:space:]]*X2:spill_reg\(t=12,reg=ar\.pfs,treg=r20\)
+[[:space:]]*X2:spill_reg\(t=13,reg=ar\.rnat,treg=r21\)
+[[:space:]]*X2:spill_reg\(t=14,reg=ar\.unat,treg=r22\)
+[[:space:]]*X2:spill_reg\(t=15,reg=psp,treg=r23\)
+[[:space:]]*X2:spill_reg\(t=16,reg=pr,treg=r24\)
+[[:space:]]*X2:spill_reg\(t=17,reg=rp,treg=r25\)
+[[:space:]]*X2:spill_reg\(t=18,reg=@priunat,treg=r26\)
+[[:space:]]*B1:label_state\(label=1\)
+[[:space:]]*B2:epilogue\(t=4,ecount=0\)
+[[:space:]]*B1:copy_state\(label=1\)
+#...
+<full2>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R2:prologue_gr\(mask=\[rp,psp,pr\],grsave=r8,rlen=14\)
+[[:space:]]*P5:frgr_mem\(grmask=\[r4,r7\],frmask=\[f2,f31\]\)
+[[:space:]]*P4:spill_mask\(imask=\[b-b,bb-,---,---,--\]\)
+[[:space:]]*P7:spill_base\(pspoff=0x10-0x10\)
+[[:space:]]*P2:br_gr\(brmask=\[b1,b5\],gr=r32\)
+[[:space:]]*X2:spill_reg\(t=6,reg=f31,treg=f31\)
+[[:space:]]*X4:spill_reg_p\(qp=p63,t=7,reg=f16,treg=f0\)
+[[:space:]]*X1:spill_sprel\(reg=f5,t=8,spoff=0x20\)
+[[:space:]]*X3:spill_sprel_p\(qp=p31,t=9,reg=f2,spoff=0x18\)
+[[:space:]]*X1:spill_psprel\(reg=b5,t=10,pspoff=0x10-0x20\)
+[[:space:]]*X3:spill_psprel_p\(qp=p15,t=11,reg=b1,pspoff=0x10-0x18\)
+[[:space:]]*X2:restore\(t=12,reg=r7\)
+[[:space:]]*X4:restore_p\(qp=p7,t=13,reg=r4\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=0\)
+[[:space:]]*R1:prologue\(rlen=0\)
+[[:space:]]*R1:body\(rlen=7\)
+[[:space:]]*B4:label_state\(label=32\)
+[[:space:]]*B3:epilogue\(t=4,ecount=32\)
+[[:space:]]*B4:copy_state\(label=32\)
+#...
+<full3>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R3:prologue\(rlen=33\)
+[[:space:]]*P4:spill_mask\(imask=\[rrb,brr,bb-,---,---,---,---,---,---,---,---\]\)
+[[:space:]]*P7:spill_base\(pspoff=0x10-0x10\)
+[[:space:]]*P9:gr_gr\(grmask=\[r4,r5\],r32\)
+[[:space:]]*P2:br_gr\(brmask=\[b1,b2\],gr=r34\)
+[[:space:]]*P9:gr_gr\(grmask=\[r6,r7\],r124\)
+[[:space:]]*P2:br_gr\(brmask=\[b4,b5\],gr=r126\)
+[[:space:]]*R3:body\(rlen=33\)
+#...
+<fframe>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=1\)
+[[:space:]]*P7:mem_stack_f\(t=0,size=0\)
+[[:space:]]*R1:body\(rlen=2\)
+#...
+<vframe>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=11\)
+[[:space:]]*P7:mem_stack_v\(t=0\)
+[[:space:]]*P3:psp_gr\(reg=r16\)
+[[:space:]]*P8:bsp_when\(t=1\)
+[[:space:]]*P3:bsp_gr\(reg=r17\)
+[[:space:]]*P8:bspstore_when\(t=2\)
+[[:space:]]*P3:bspstore_gr\(reg=r18\)
+[[:space:]]*P7:fpsr_when\(t=3\)
+[[:space:]]*P3:fpsr_gr\(reg=r19\)
+[[:space:]]*P7:lc_when\(t=4\)
+[[:space:]]*P3:lc_gr\(reg=r20\)
+[[:space:]]*P7:pfs_when\(t=5\)
+[[:space:]]*P3:pfs_gr\(reg=r21\)
+[[:space:]]*P8:rnat_when\(t=6\)
+[[:space:]]*P3:rnat_gr\(reg=r22\)
+[[:space:]]*P7:unat_when\(t=7\)
+[[:space:]]*P3:unat_gr\(reg=r23\)
+[[:space:]]*P7:pr_when\(t=8\)
+[[:space:]]*P3:pr_gr\(reg=r24\)
+[[:space:]]*P8:priunat_when_gr\(t=9\)
+[[:space:]]*P3:priunat_gr\(reg=r25\)
+[[:space:]]*P7:rp_when\(t=10\)
+[[:space:]]*P3:rp_gr\(reg=r26\)
+[[:space:]]*R1:body\(rlen=1\)
+#...
+<vframesp>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=11\)
+[[:space:]]*P7:mem_stack_v\(t=0\)
+[[:space:]]*P7:psp_sprel\(spoff=0x0\)
+[[:space:]]*P8:bsp_when\(t=1\)
+[[:space:]]*P8:bsp_sprel\(spoff=0x8\)
+[[:space:]]*P8:bspstore_when\(t=2\)
+[[:space:]]*P8:bspstore_sprel\(spoff=0x10\)
+[[:space:]]*P7:fpsr_when\(t=3\)
+[[:space:]]*P8:fpsr_sprel\(spoff=0x18\)
+[[:space:]]*P7:lc_when\(t=4\)
+[[:space:]]*P8:lc_sprel\(spoff=0x20\)
+[[:space:]]*P7:pfs_when\(t=5\)
+[[:space:]]*P8:pfs_sprel\(spoff=0x28\)
+[[:space:]]*P8:rnat_when\(t=6\)
+[[:space:]]*P8:rnat_sprel\(spoff=0x30\)
+[[:space:]]*P7:unat_when\(t=7\)
+[[:space:]]*P8:unat_sprel\(spoff=0x38\)
+[[:space:]]*P7:pr_when\(t=8\)
+[[:space:]]*P8:pr_sprel\(spoff=0x40\)
+[[:space:]]*P8:priunat_when_mem\(t=9\)
+[[:space:]]*P8:priunat_sprel\(spoff=0x48\)
+[[:space:]]*P7:rp_when\(t=10\)
+[[:space:]]*P8:rp_sprel\(spoff=0x50\)
+[[:space:]]*R1:body\(rlen=1\)
+#...
+<psp>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+[[:space:]]*R1:prologue\(rlen=11\)
+[[:space:]]*P7:mem_stack_v\(t=0\)
+[[:space:]]*P7:psp_sprel\(spoff=0x0\)
+[[:space:]]*P8:bsp_when\(t=1\)
+[[:space:]]*P8:bsp_psprel\(pspoff=0x10-0x18\)
+[[:space:]]*P8:bspstore_when\(t=2\)
+[[:space:]]*P8:bspstore_psprel\(pspoff=0x10-0x20\)
+[[:space:]]*P7:fpsr_when\(t=3\)
+[[:space:]]*P7:fpsr_psprel\(pspoff=0x10-0x28\)
+[[:space:]]*P7:lc_when\(t=4\)
+[[:space:]]*P7:lc_psprel\(pspoff=0x10-0x30\)
+[[:space:]]*P7:pfs_when\(t=5\)
+[[:space:]]*P7:pfs_psprel\(pspoff=0x10-0x38\)
+[[:space:]]*P8:rnat_when\(t=6\)
+[[:space:]]*P8:rnat_psprel\(pspoff=0x10-0x40\)
+[[:space:]]*P7:unat_when\(t=7\)
+[[:space:]]*P7:unat_psprel\(pspoff=0x10-0x48\)
+[[:space:]]*P7:pr_when\(t=8\)
+[[:space:]]*P7:pr_psprel\(pspoff=0x10-0x50\)
+[[:space:]]*P8:priunat_when_mem\(t=9\)
+[[:space:]]*P8:priunat_psprel\(pspoff=0x10-0x58\)
+[[:space:]]*P7:rp_when\(t=10\)
+[[:space:]]*P7:rp_psprel\(pspoff=0x10-0x60\)
+[[:space:]]*R1:body\(rlen=1\)
+#...
+<simple>: \[0x[[:xdigit:]]*0-0x[[:xdigit:]]*0\], info at \+0x[[:xdigit:]]*[08]
+[[:space:]]*v[[:digit:]]+, flags=0x0( \(\))?, len=[[:digit:]]+ bytes
+#pass
--- /home/jbeulich/src/binutils/mainline/2005-06-27/gas/testsuite/gas/ia64/unwind-ok.s	1970-01-01 01:00:00.000000000 +0100
+++ 2005-06-27/gas/testsuite/gas/ia64/unwind-ok.s	2005-06-27 11:36:58.973583898 +0200
@@ -0,0 +1,272 @@
+.text
+.proc personality
+personality:
+	br.ret.sptk	rp
+.endp personality
+
+.proc full1
+full1:
+
+.prologue
+.spill 0
+.save.g 0x1
+	nop		0
+.save.f 0x1
+	nop		0
+.save.b 0x01
+	nop		0
+.save.g 0x8
+	nop		0
+.save.f 0x8
+	nop		0
+.save.b 0x10
+	nop		0
+.altrp b7
+	nop		0
+.unwabi @svr4, 0
+	nop		0
+
+.body
+.spillreg r4, r2
+	nop		0
+.spillreg.p p1, r7, r127
+	nop		0
+.spillsp b1, 0x08
+	nop		0
+.spillsp.p p2, b5, 0x10
+	nop		0
+.spillpsp f2, 0x18
+	nop		0
+.spillpsp.p p4, f5, 0x20
+	nop		0
+.restorereg f16
+	nop		0
+.restorereg.p p8, f31
+	nop		0
+
+.spillreg ar.bsp, r16
+	nop		0
+.spillreg ar.bspstore, r17
+	nop		0
+.spillreg ar.fpsr, r18
+	nop		0
+.spillreg ar.lc, r19
+	nop		0
+.spillreg ar.pfs, r20
+	nop		0
+.spillreg ar.rnat, r21
+	nop		0
+.spillreg ar.unat, r22
+	nop		0
+.spillreg psp, r23
+	nop		0
+.spillreg pr, r24
+	nop		0
+.spillreg rp, r25
+	nop		0
+.spillreg @priunat, r26
+	nop		0
+
+.label_state 1
+	nop		0
+.restore sp
+	nop.x		0
+.copy_state 1
+	br.ret.sptk	rp
+
+.personality personality
+.handlerdata
+	data4		-1
+	data4		0
+
+.endp full1
+
+.proc full2
+full2:
+
+.prologue 0xb, r8
+.spill 0
+.save.gf 0x1, 0x00001
+	nop		0
+	nop		0
+.save.b 0x11, r32
+	nop		0
+	nop		0
+.save.gf 0x8, 0x80000
+	nop		0
+	nop		0
+.spillreg f31, f127
+	nop		0
+.spillreg.p p63, f16, f32
+	nop		0
+.spillsp f5, 0x20
+	nop		0
+.spillsp.p p31, f2, 0x18
+	nop		0
+.spillpsp b5, 0x10
+	nop		0
+.spillpsp.p p15, b1, 0x08
+	nop		0
+.restorereg r7
+	nop		0
+.restorereg.p p7, r4
+	nop		0
+
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+.body; .prologue; .body; .prologue; .body; .prologue; .body; .prologue
+
+.body
+.label_state 32
+	nop		0
+.restore sp, 32
+	nop.x		0
+.copy_state 32
+	br.ret.sptk	rp
+.endp full2
+
+.proc full3
+full3:
+
+.prologue
+.spill 0
+.save.g 0x3, r32
+	nop		0
+	nop		0
+.save.b 0x03, r34
+	nop		0
+	nop		0
+.save.g 0xc, r124
+	nop		0
+	nop		0
+.save.b 0x18, r126
+	nop		0
+	nop		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+.body
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	nop.x		0
+	br.ret.sptk	rp
+.endp full3
+
+.proc fframe
+fframe:
+.prologue
+.fframe 0
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp fframe
+
+.proc vframe
+vframe:
+.prologue
+.vframe r16
+	nop		0
+.save ar.bsp, r17
+	nop		0
+.save ar.bspstore, r18
+	nop		0
+.save ar.fpsr, r19
+	nop		0
+.save ar.lc, r20
+	nop		0
+.save ar.pfs, r21
+	nop		0
+.save ar.rnat, r22
+	nop		0
+.save ar.unat, r23
+	nop		0
+.save pr, r24
+	nop		0
+.save @priunat, r25
+	nop		0
+.save rp, r26
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp vframe
+
+.proc vframesp
+vframesp:
+.prologue
+.vframesp 0
+	nop		0
+.savesp ar.bsp, 0x08
+	nop		0
+.savesp ar.bspstore, 0x10
+	nop		0
+.savesp ar.fpsr, 0x18
+	nop		0
+.savesp ar.lc, 0x20
+	nop		0
+.savesp ar.pfs, 0x28
+	nop		0
+.savesp ar.rnat, 0x30
+	nop		0
+.savesp ar.unat, 0x38
+	nop		0
+.savesp pr, 0x40
+	nop		0
+.savesp @priunat, 0x48
+	nop		0
+.savesp rp, 0x50
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp vframesp
+
+.proc psp
+psp:
+.prologue
+.vframesp 0
+	nop		0
+.savepsp ar.bsp, 0x08
+	nop		0
+.savepsp ar.bspstore, 0x10
+	nop		0
+.savepsp ar.fpsr, 0x18
+	nop		0
+.savepsp ar.lc, 0x20
+	nop		0
+.savepsp ar.pfs, 0x28
+	nop		0
+.savepsp ar.rnat, 0x30
+	nop		0
+.savepsp ar.unat, 0x38
+	nop		0
+.savepsp pr, 0x40
+	nop		0
+.savepsp @priunat, 0x48
+	nop		0
+.savepsp rp, 0x50
+	nop		0
+.body
+	br.ret.sptk	rp
+.endp psp
+
+.proc simple
+simple:
+.unwentry
+	br.ret.sptk	rp
+.endp simple

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] ia64: unwind directive handling
@ 2005-06-15 15:31 Jan Beulich
  0 siblings, 0 replies; 8+ messages in thread
From: Jan Beulich @ 2005-06-15 15:31 UTC (permalink / raw)
  To: wilson; +Cc: cary, david_mosberger, binutils

So Jim, should I remove .vframepsp altogether in the to-be-updated the patch? Jan

>>> "Jan Beulich" <JBeulich@novell.com> 13.06.05 09:06:52 >>>
>>> Cary Coutant <cary@cup.hp.com> 10.06.05 23:52:01 >>>
>> I checked the SCRA.  Appendix B constains tables listing all of the 
>> valid
>> unwind encodings.  This does not include psp_psprel.  I see that it 
>> comes
>> from the .vframepsp directive.  Curiously, the asm language manual has 
>> two
>> typos in the vframepsp docs.  It emits two unwind records, and both of 
>> them
>> are mispelled.  Maybe this is a late change that was never properly 
>> documented.
>> How do you know that P8 r=0 is correct?
>
>Jim is right -- there is no psp_psprel unwind directive, and Format P8, 
>r=0 is unassigned.

As I said, ias uses this, so it can't be completely invalid.

>If you think about it, storing the previous stack pointer at a location 
>relative to psp is pointless at best, since you would need to know the 
>psp in order to recover the psp!

Hmm, indeed. This, however, still doesn't mean no change is needed; .vframepsp itself is pointless, then, and support for it should be removed.

Jan


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] ia64: unwind directive handling
@ 2005-06-13  7:07 Jan Beulich
  0 siblings, 0 replies; 8+ messages in thread
From: Jan Beulich @ 2005-06-13  7:07 UTC (permalink / raw)
  To: cary, wilson; +Cc: david_mosberger, binutils

>>> Cary Coutant <cary@cup.hp.com> 10.06.05 23:52:01 >>>
>> I checked the SCRA.  Appendix B constains tables listing all of the 
>> valid
>> unwind encodings.  This does not include psp_psprel.  I see that it 
>> comes
>> from the .vframepsp directive.  Curiously, the asm language manual has 
>> two
>> typos in the vframepsp docs.  It emits two unwind records, and both of 
>> them
>> are mispelled.  Maybe this is a late change that was never properly 
>> documented.
>> How do you know that P8 r=0 is correct?
>
>Jim is right -- there is no psp_psprel unwind directive, and Format P8, 
>r=0 is unassigned.

As I said, ias uses this, so it can't be completely invalid.

>If you think about it, storing the previous stack pointer at a location 
>relative to psp is pointless at best, since you would need to know the 
>psp in order to recover the psp!

Hmm, indeed. This, however, still doesn't mean no change is needed; .vframepsp itself is pointless, then, and support for it should be removed.

Jan

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] ia64: unwind directive handling
@ 2005-06-10 21:52 Cary Coutant
  0 siblings, 0 replies; 8+ messages in thread
From: Cary Coutant @ 2005-06-10 21:52 UTC (permalink / raw)
  To: wilson; +Cc: binutils, JBeulich, David Mosberger

> I checked the SCRA.  Appendix B constains tables listing all of the 
> valid
> unwind encodings.  This does not include psp_psprel.  I see that it 
> comes
> from the .vframepsp directive.  Curiously, the asm language manual has 
> two
> typos in the vframepsp docs.  It emits two unwind records, and both of 
> them
> are mispelled.  Maybe this is a late change that was never properly 
> documented.
> How do you know that P8 r=0 is correct?

Jim is right -- there is no psp_psprel unwind directive, and Format P8, 
r=0 is unassigned.

If you think about it, storing the previous stack pointer at a location 
relative to psp is pointless at best, since you would need to know the 
psp in order to recover the psp!

Cary Coutant
IA-64 Runtime Architect
Hewlett-Packard Co.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] ia64: unwind directive handling
@ 2005-06-10 13:21 Jan Beulich
  0 siblings, 0 replies; 8+ messages in thread
From: Jan Beulich @ 2005-06-10 13:21 UTC (permalink / raw)
  To: wilson; +Cc: binutils

>For .prologue, the assembler docs appear to be ambiguous.  It states that the
>second operand to .prologue is grsave, and then in the following section it
>defines what grsave means, but no where does it clearly define how grsave is
>represented in the assembler file.  Still, it does seem a bit odd that we ended
>up using a constant instead of a register name.  That was probably a historical
>mistake.

I'm reading their (broken) wording "grsave saves the rp, ar.pfs, psp, and pr register contents to the first general-purpose
register" as implying a gr here, not a constant. Anyway, ias doesn't accept a constant here, and hence the requirement to at least accept a register in gas. The use of the immediate form in gcc is why I made, for the time being, the deprecation warning conditional upon unwind_check_error.

>You are changing unwind-ia64.c.  There are also copies of this code in the
>linux kernel and in the libunwind package.  So if we change this file, we need
>to let David Mosberger know, so he can fix the other two occurances.  And there
>is also the gcc unwinder that would need to be fixed.  There is the more
>general question of whether any unwinder supports psp_psprel.  If no unwinder
>supports it, then it doesn't serve much purpose for gas to support it.

Hmm, I understand your concern here, but the code previously issued a psp_sprel for a .vframepsp, which definitely is broken. So I continue to think the change is needed, and I clearly agree that kernel, libunwind, and whoever else implements this.

I actually have an unwinder that has been knowing of this encoding since its initial implementation.

>I checked the SCRA.  Appendix B constains tables listing all of the valid
>unwind encodings.  This does not include psp_psprel.  I see that it comes
>from the .vframepsp directive.  Curiously, the asm language manual has two
>typos in the vframepsp docs.  It emits two unwind records, and both of them
>are mispelled.  Maybe this is a late change that was never properly documented.
>How do you know that P8 r=0 is correct?  You confirmed this from ias?  It would
>be useful to get confirmation that this is actually supposed to exist, and
>that we are actually supposed to implement it.  We should report these doc
>bugs to Intel.  The asm language manual has so many bugs that probably no one
>cares about it, but bugs in the SCRA are serious, as it is used for ABI
>conformance.  The SCRA must be fixed if it is wrong.

The knowledge I have is from ias output. That used to be quite helpful when working through all the typos/omissions in the doc. I have no other explicit confirmation from Intel, and my experience with other issues we discussed here and I brought up to them is that they get virtually never addressed. So if we wanted to wait for their confirmation, we well may wait forever. But to me this is so obvious from all the facts we have; I always considered this just a typographic issue with an omitted line in the relevant table.

>As for '}' as a line separator, as usual, this isn't documented in the
>asm language manual.  Unfortunately, this means that we are now handling '{'
>and '}' differently.  This begs the question of whether perhaps '{' can also
>be a line separator.  Did you try that?  If so, then that would allow us to
>handle them both the same way again.

Interesting, I didn't even think about also making '{' a line terminator. ias indeed also treats it like that. I'll have to rework the patch then.

>Putting a switch default case in the middle looks odd to me.  Typically, it is
>at the end, after all of the cases.  That makes it easier to read the code,
>since it is easy to see that the default case is there.  This is done in
>dot_save and dot_savemem.

I considered that the more desirable solution over duplicating code (and I'm a strong opponent of goto-s, so I'd rather not use them for avoiding duplicate code).

>I notice that you allow r0 as a valid for a gr-location in a .save directive.
>Does this ever make sense?  I noticed this because you deliberately disallow
>r0 in other places, such as the first option to .spillreg, but checking the
>docs, I see that the docs explicitly say that only certain registers are valid
>here.  However, the docs don't appear to say anything about using r0 for the
>destination (gr-location) of a .save directive.  This is probably OK as it is.

Yes, this is intentional, for two reasons. First, this matches ias behavior (and using r0 in .spillreg, other than in .save, actually may cause degenerated unwind records to be emitted, because the .restorereg utilizes an r0-encoding). Second, there indeed is a use of this in .save (and if the just mentioned problem with the encoding didn't exist this could apply to .spillreg as well): When you have a function at the bottom of a call tree (i.e. the boot code of an OS or the initial thread of a process) you can use ".save rp, r0" to indicate the end of the stack to the unwinder. I used this when we brought up an OS prototype during the early days of ia64, and I'm fairly certain other people use this, too.

>I see you are adding a popcnt function into the middle of the tc-ia64.c file.
>This does look a little wierd.  I don't have any strong objection to it, except
>to point out that the GNU coding standards require that every function have
>an explanatory comment that describes what the function does and you didn't
>add one.

Adding a comment is no problem, and as I indicate in the comment preceding it I'm not happy with the placement either. Just I don't know where to put it. Of course this could live in libiberty, but I'm not sure I actually want to touch this component unless I absolutely can't avoid it.

>In dot_saveg, you have
>   grmask = e.X_add_number;
>then you test the value in e.X_add_number.  I think that makes more sense as
>tests again grmask, and that saves some memory references.  Similarly in
>dot_saveb.

Again, that was intentional: Once converted to unsigned (the type grmask has) there may (almost certainly will) have been top bits stripped off, and that prevents detecting all invalid uses if these bits were non-zero. I was actually thinking in the opposite direction - change other functions to not use the converted values, too.

>I see that you have bugs documented as comments in the file
>gas/testsuite/gas/ia64/unwind-bad.l.  That is a very unusual place and way
>to document bugs.  It would be better if this was documented someplace more
>visible.  Probably no one is ever going to notice that the bugs are documented
>here.  I'm also left wondering how you generated this file.  How do you know
>that these error messages are missing?  What does the "previous spill
>incomplete" message mean?  It looks almost like you were copying from
>something else, which might be copyright infringement.  

That's actually stuff I intended to work on once this patch here has been dealt with. The overlaps of subsequent changes would just be too large to deal with. The spelling used is just the one I intended to use for the diagnostics.
The meaning of the "previous spill incomplete", similar to a diagnostic issued by ias, is that there cannot reasonably be unwind directives following a .saveg/.savegf/.savef/.saveb while there weren't sufficiently many instructions seen to match the number of bits set in their respective operands. I actually suspect, without having checked, that there are even more severe problems with multi-bit uses of these directives; checking and if necessary solving these was my intention together with adding these new diagnostics.

>I noticed that you change unwabi from in_procedure to in_prologue.  Do you have
>a reason for that?  I also noticed that unwabi is missing from the SCRA, which
>is a serious bug, though the linux kernel and libunwind already support unwabi
>so that isn't a problem.

I can't see this missing from SCRA, it's format P10 (it just doesn't mention the word unwabi anywhere). Since this is a prologue region record, the originally added check was insufficient.

>Otherwise this looks reasonable to me.  I think most of these issues are
>pretty minor except the psp_psprel, and perhaps the unwind-bad.l issue
>(depending on what the answer is).

Before doing the rework, I'll wait a couple of days to see whether you have any further comments,
Jan

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2005-07-01  3:10 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-05-23 10:19 [PATCH] ia64: unwind directive handling Jan Beulich
2005-06-10  0:07 ` James E Wilson
2005-06-10 13:21 Jan Beulich
2005-06-10 21:52 Cary Coutant
2005-06-13  7:07 Jan Beulich
2005-06-15 15:31 Jan Beulich
2005-06-27 10:31 Jan Beulich
2005-07-01  3:10 ` James E Wilson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).