/* These are used to create the unwind table entry for this function.  */
   symbolS *proc_start;
-  symbolS *proc_end;
   symbolS *info;               /* pointer to unwind info */
   symbolS *personality_routine;
   segT saved_text_seg;
   unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */
 
   /* TRUE if processing unwind directives in a prologue region.  */
-  int prologue;
-  int prologue_mask;
+  unsigned int prologue : 1;
+  unsigned int prologue_mask : 4;
+  unsigned int body : 1;
+  unsigned int insn : 1;
   unsigned int prologue_count; /* number of .prologues seen so far */
   /* Prologue counts at previous .label_state directives.  */
   struct label_prologue_count * saved_prologue_counts;
        case frgr_mem:
          if (!region)
            {
-             as_bad ("frgr_mem record before region record!\n");
+             as_bad ("frgr_mem record before region record!");
              return;
            }
          region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask;
        case fr_mem:
          if (!region)
            {
-             as_bad ("fr_mem record before region record!\n");
+             as_bad ("fr_mem record before region record!");
              return;
            }
          region->r.record.r.mask.fr_mem |= ptr->r.record.p.rmask;
        case gr_mem:
          if (!region)
            {
-             as_bad ("gr_mem record before region record!\n");
+             as_bad ("gr_mem record before region record!");
              return;
            }
          region->r.record.r.mask.gr_mem |= ptr->r.record.p.rmask;
        case br_mem:
          if (!region)
            {
-             as_bad ("br_mem record before region record!\n");
+             as_bad ("br_mem record before region record!");
              return;
            }
          region->r.record.r.mask.br_mem |= ptr->r.record.p.brmask;
        case gr_gr:
          if (!region)
            {
-             as_bad ("gr_gr record before region record!\n");
+             as_bad ("gr_gr record before region record!");
              return;
            }
          set_imask (region, ptr->r.record.p.grmask, t, 2);
        case br_gr:
          if (!region)
            {
-             as_bad ("br_gr record before region record!\n");
+             as_bad ("br_gr record before region record!");
              return;
            }
          set_imask (region, ptr->r.record.p.brmask, t, 3);
   set_section ((char *) special_section_name[which]);
 }
 
+static int
+in_procedure (const char *directive)
+{
+  if (unwind.proc_start
+      && (!unwind.saved_text_seg || strcmp (directive, "endp") == 0))
+    return 1;
+  as_bad (".%s outside of procedure", directive);
+  ignore_rest_of_line ();
+  return 0;
+}
+
+static int
+in_prologue (const char *directive)
+{
+  if (in_procedure (directive))
+    {
+      if (unwind.prologue)
+       return 1;
+      as_bad (".%s outside of prologue", directive);
+      ignore_rest_of_line ();
+    }
+  return 0;
+}
+
+static int
+in_body (const char *directive)
+{
+  if (in_procedure (directive))
+    {
+      if (unwind.body)
+       return 1;
+      as_bad (".%s outside of body region", directive);
+      ignore_rest_of_line ();
+    }
+  return 0;
+}
+
 static void
 add_unwind_entry (ptr)
      unw_rec_list *ptr;
 {
   expressionS e;
 
+  if (!in_prologue ("fframe"))
+    return;
+
   parse_operand (&e);
 
   if (e.X_op != O_constant)
   expressionS e;
   unsigned reg;
 
+  if (!in_prologue ("vframe"))
+    return;
+
   parse_operand (&e);
   reg = e.X_add_number - REG_GR;
   if (e.X_op == O_register && reg < 128)
 {
   expressionS e;
 
+  if (!in_prologue ("vframesp"))
+    return;
+
   parse_operand (&e);
   if (e.X_op == O_constant)
     {
 {
   expressionS e;
 
+  if (!in_prologue ("vframepsp"))
+    return;
+
   parse_operand (&e);
   if (e.X_op == O_constant)
     {
   int sep;
   int reg1, reg2;
 
+  if (!in_prologue ("save"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     as_bad ("No second operand to .save");
   unsigned long ecount;        /* # of _additional_ regions to pop */
   int sep;
 
+  if (!in_body ("restore"))
+    return;
+
   sep = parse_operand (&e1);
   if (e1.X_op != O_register || e1.X_add_number != REG_GR + 12)
     {
   unsigned int ab, reg;
   expressionS e;
 
+  if (!in_procedure ("restorereg"))
+    return;
+
   parse_operand (&e);
 
   if (!convert_expr_to_ab_reg (&e, &ab, ®))
   expressionS e1, e2;
   int sep;
 
+  if (!in_procedure ("restorereg.p"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
 dot_handlerdata (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("handlerdata"))
+    return;
   unwind.force_unwind_entry = 1;
 
   /* Remember which segment we're in so we can switch back after .endp */
 dot_unwentry (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("unwentry"))
+    return;
   unwind.force_unwind_entry = 1;
   demand_empty_rest_of_line ();
 }
   expressionS e;
   unsigned reg;
 
+  if (!in_prologue ("altrp"))
+    return;
+
   parse_operand (&e);
   reg = e.X_add_number - REG_BR;
   if (e.X_op == O_register && reg < 8)
   int sep;
   int reg1, val;
 
+  if (!in_prologue (psprel ? "savepsp" : "savesp"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     as_bad ("No second operand to .save%ssp", psprel ? "p" : "");
 {
   expressionS e1, e2;
   int sep;
+
+  if (!in_prologue ("save.g"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep == ',')
     parse_operand (&e2);
 {
   expressionS e1;
   int sep;
+
+  if (!in_prologue ("save.f"))
+    return;
+
   sep = parse_operand (&e1);
 
   if (e1.X_op != O_constant)
   unsigned char sep;
   int brmask;
 
+  if (!in_prologue ("save.b"))
+    return;
+
   sep = parse_operand (&e1);
   if (e1.X_op != O_constant)
     {
 {
   expressionS e1, e2;
   int sep;
+
+  if (!in_prologue ("save.gf"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep == ',')
     parse_operand (&e2);
   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 ();
   int sep, ab, xy, reg, treg;
   expressionS e1, e2;
 
+  if (!in_procedure ("spillreg"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
   expressionS e1, e2;
   int sep, ab, reg;
 
+  if (!in_procedure ("spillmem"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
   expressionS e1, e2, e3;
   unsigned int qp;
 
+  if (!in_procedure ("spillreg.p"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
   int sep, ab, reg;
   unsigned int qp;
 
+  if (!in_procedure ("spillmem.p"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
 {
   expressionS e;
 
+  if (!in_body ("label_state"))
+    return;
+
   parse_operand (&e);
   if (e.X_op != O_constant)
     {
 {
   expressionS e;
 
+  if (!in_body ("copy_state"))
+    return;
+
   parse_operand (&e);
   if (e.X_op != O_constant)
     {
   expressionS e1, e2;
   unsigned char sep;
 
+  if (!in_procedure ("unwabi"))
+    return;
+
   sep = parse_operand (&e1);
   if (sep != ',')
     {
      int dummy ATTRIBUTE_UNUSED;
 {
   char *name, *p, c;
+  if (!in_procedure ("personality"))
+    return;
   SKIP_WHITESPACE ();
   name = input_line_pointer;
   c = get_symbol_end ();
   char *name, *p, c;
   symbolS *sym;
 
-  unwind.proc_start = expr_build_dot ();
+  unwind.proc_start = 0;
   /* Parse names of main and alternate entry points and mark them as
      function symbols:  */
   while (1)
       name = input_line_pointer;
       c = get_symbol_end ();
       p = input_line_pointer;
-      sym = symbol_find_or_make (name);
-      if (unwind.proc_start == 0)
+      if (!*name)
+       as_bad ("Empty argument of .proc");
+      else
        {
-         unwind.proc_start = sym;
+         sym = symbol_find_or_make (name);
+         if (S_IS_DEFINED (sym))
+           as_bad ("`%s' was already defined", name);
+         else if (unwind.proc_start == 0)
+           {
+             unwind.proc_start = sym;
+           }
+         symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
        }
-      symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
       *p = c;
       SKIP_WHITESPACE ();
       if (*input_line_pointer != ',')
        break;
       ++input_line_pointer;
     }
+  if (unwind.proc_start == 0)
+    unwind.proc_start = expr_build_dot ();
   demand_empty_rest_of_line ();
   ia64_do_align (16);
 
+  unwind.prologue = 0;
   unwind.prologue_count = 0;
+  unwind.body = 0;
+  unwind.insn = 0;
   unwind.list = unwind.tail = unwind.current_entry = NULL;
   unwind.personality_routine = 0;
 }
 dot_body (dummy)
      int dummy ATTRIBUTE_UNUSED;
 {
+  if (!in_procedure ("body"))
+    return;
+  if (!unwind.prologue && !unwind.body && unwind.insn)
+    as_warn ("Initial .body should precede any instructions");
+
   unwind.prologue = 0;
   unwind.prologue_mask = 0;
+  unwind.body = 1;
 
   add_unwind_entry (output_body ());
   demand_empty_rest_of_line ();
   unsigned char sep;
   int mask = 0, grsave = 0;
 
+  if (!in_procedure ("prologue"))
+    return;
+  if (unwind.prologue)
+    {
+      as_bad (".prologue within prologue");
+      ignore_rest_of_line ();
+      return;
+    }
+  if (!unwind.body && unwind.insn)
+    as_warn ("Initial .prologue should precede any instructions");
+
   if (!is_it_end_of_statement ())
     {
       expressionS e1, e2;
 
   unwind.prologue = 1;
   unwind.prologue_mask = mask;
+  unwind.body = 0;
   ++unwind.prologue_count;
 }
 
   char *name, *p, c;
   symbolS *sym;
 
+  if (!in_procedure ("endp"))
+    return;
+
   if (unwind.saved_text_seg)
     {
       saved_seg = unwind.saved_text_seg;
 
   if (unwind.info || unwind.force_unwind_entry)
     {
+      symbolS *proc_end;
+
       subseg_set (md.last_text_seg, 0);
-      unwind.proc_end = expr_build_dot ();
+      proc_end = expr_build_dot ();
 
       start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND, 0);
 
       e.X_op = O_pseudo_fixup;
       e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
       e.X_add_number = 0;
-      e.X_add_symbol = unwind.proc_end;
+      e.X_add_symbol = proc_end;
       ia64_cons_fix_new (frag_now, where + bytes_per_address,
                         bytes_per_address, &e);
 
       name = input_line_pointer;
       c = get_symbol_end ();
       p = input_line_pointer;
-      sym = symbol_find (name);
-      if (sym && unwind.proc_start
-         && (symbol_get_bfdsym (sym)->flags & BSF_FUNCTION)
-         && S_GET_SIZE (sym) == 0 && symbol_get_obj (sym)->size == NULL)
-       {
-         fragS *fr = symbol_get_frag (unwind.proc_start);
-         fragS *frag = symbol_get_frag (sym);
-
-         /* Check whether the function label is at or beyond last
-            .proc directive.  */
-         while (fr && fr != frag)
-           fr = fr->fr_next;
-         if (fr)
-           {
-             if (frag == frag_now && SEG_NORMAL (now_seg))
-               S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym));
-             else
+      if (!*name)
+       as_bad ("Empty argument of .endp");
+      else
+       {
+         sym = symbol_find (name);
+         if (!sym || !S_IS_DEFINED (sym))
+           as_bad ("`%s' was not defined within procedure", name);
+         else if (unwind.proc_start
+             && (symbol_get_bfdsym (sym)->flags & BSF_FUNCTION)
+             && S_GET_SIZE (sym) == 0 && symbol_get_obj (sym)->size == NULL)
+           {
+             fragS *fr = symbol_get_frag (unwind.proc_start);
+             fragS *frag = symbol_get_frag (sym);
+
+             /* Check whether the function label is at or beyond last
+                .proc directive.  */
+             while (fr && fr != frag)
+               fr = fr->fr_next;
+             if (fr)
                {
-                 symbol_get_obj (sym)->size =
-                   (expressionS *) xmalloc (sizeof (expressionS));
-                 symbol_get_obj (sym)->size->X_op = O_subtract;
-                 symbol_get_obj (sym)->size->X_add_symbol
-                   = symbol_new (FAKE_LABEL_NAME, now_seg,
-                                 frag_now_fix (), frag_now);
-                 symbol_get_obj (sym)->size->X_op_symbol = sym;
-                 symbol_get_obj (sym)->size->X_add_number = 0;
+                 if (frag == frag_now && SEG_NORMAL (now_seg))
+                   S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym));
+                 else
+                   {
+                     symbol_get_obj (sym)->size =
+                       (expressionS *) xmalloc (sizeof (expressionS));
+                     symbol_get_obj (sym)->size->X_op = O_subtract;
+                     symbol_get_obj (sym)->size->X_add_symbol
+                       = symbol_new (FAKE_LABEL_NAME, now_seg,
+                                     frag_now_fix (), frag_now);
+                     symbol_get_obj (sym)->size->X_op_symbol = sym;
+                     symbol_get_obj (sym)->size->X_add_number = 0;
+                   }
                }
            }
        }
       ++input_line_pointer;
     }
   demand_empty_rest_of_line ();
-  unwind.proc_start = unwind.proc_end = unwind.info = 0;
+  unwind.proc_start = unwind.info = 0;
 }
 
 static void
       CURR_SLOT.unwind_record = unwind.current_entry;
       unwind.current_entry = NULL;
     }
+  if (unwind.proc_start && S_IS_DEFINED (unwind.proc_start))
+    unwind.insn = 1;
 
   /* Check for dependency violations.  */
   if (md.detect_dv)