OSDN Git Service

(notice_cc_update): Set CC_FCOMI is this is a float compare.
[pf3gnuchains/gcc-fork.git] / gcc / stmt.c
index 21e4932..6d78234 100644 (file)
@@ -1,5 +1,5 @@
 /* Expands front end tree to back end RTL for GNU C-Compiler
-   Copyright (C) 1987, 88, 89, 92-5, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 89, 92-6, 1997 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -129,12 +129,10 @@ extern rtx arg_pointer_save_area;
 /* Chain of all RTL_EXPRs that have insns in them.  */
 extern tree rtl_expr_chain;
 
-#if 0  /* Turned off because 0 seems to work just as well.  */
-/* Cleanup lists are required for binding levels regardless of whether
-   that binding level has cleanups or not.  This node serves as the
-   cleanup list whenever an empty list is required.  */
-static tree empty_cleanup_list;
-#endif
+/* Stack allocation level in which temporaries for TARGET_EXPRs live.  */
+extern int target_temp_slot_level;
+
+extern int temp_slot_level;
 \f
 /* Functions and data structures for expanding case statements.  */
 
@@ -262,7 +260,7 @@ struct nesting
             as they were at the locus where this block appears.
             There is an element for each containing block,
             ordered innermost containing block first.
-            The tail of this list can be 0 (was empty_cleanup_list),
+            The tail of this list can be 0,
             if all remaining elements would be empty lists.
             The element's TREE_VALUE is the cleanup-list of that block,
             which may be null.  */
@@ -274,6 +272,28 @@ struct nesting
          int function_call_count;
          /* Bytecode specific: stack level to restore stack to on exit.  */
          int bc_stack_level;
+         /* Nonzero if this is associated with a EH region.  */
+         int exception_region;
+         /* The saved target_temp_slot_level from our outer block.
+            We may reset target_temp_slot_level to be the level of
+            this block, if that is done, target_temp_slot_level
+            reverts to the saved target_temp_slot_level at the very
+            end of the block.  */
+         int target_temp_slot_level;
+         /* True if we are currently emitting insns in an area of
+            output code that is controlled by a conditional
+            expression.  This is used by the cleanup handling code to
+            generate conditional cleanup actions.  */
+         int conditional_code;
+         /* A place to move the start of the exception region for any
+            of the conditional cleanups, must be at the end or after
+            the start of the last unconditional cleanup, and before any
+            conditional branch points.  */
+         rtx last_unconditional_cleanup;
+         /* When in a conditional context, this is the specific
+            cleanup list associated with last_unconditional_cleanup,
+            where we place the conditionalized cleanups.  */
+         tree *cleanup_ptr;
        } block;
       /* For switch (C) or case (Pascal) statements,
         and also for dummies (see `expand_start_case_dummy').  */
@@ -391,7 +411,7 @@ struct goto_fixup
   rtx stack_level;
   /* List of lists of cleanup expressions to be run by this goto.
      There is one element for each block that this goto is within.
-     The tail of this list can be 0 (was empty_cleanup_list),
+     The tail of this list can be 0,
      if all remaining elements would be empty.
      The TREE_VALUE contains the cleanup list of that block as of the
      time this goto was seen.
@@ -1021,11 +1041,7 @@ expand_fixup (tree_label, rtl_label, last_insn)
       fixup->block_start_count = block_start_count;
       fixup->stack_level = 0;
       fixup->cleanup_list_list
-       = (((block->data.block.outer_cleanups
-#if 0
-            && block->data.block.outer_cleanups != empty_cleanup_list
-#endif
-            )
+       = ((block->data.block.outer_cleanups
            || block->data.block.cleanups)
           ? tree_cons (NULL_TREE, block->data.block.cleanups,
                        block->data.block.outer_cleanups)
@@ -1301,7 +1317,7 @@ bc_fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in)
       /* Emit code to restore the stack and continue */
       bc_emit_bytecode_labeldef (f->label);
 
-      /* Save stack_depth across call, since bc_adjust_stack () will alter
+      /* Save stack_depth across call, since bc_adjust_stack will alter
          the perceived stack depth via the instructions generated.  */
 
       if (f->bc_stack_level >= 0)
@@ -1453,13 +1469,12 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
 #endif
            break;
 
-         case 'p':  case 'g':  case 'r':
-           /* Whether or not a numeric constraint allows a register is
-              decided by the matching constraint, and so there is no need
-              to do anything special with them.  We must handle them in
-              the default case, so that we don't unnecessarily force
-              operands to memory.  */
          case '0':  case '1':  case '2':  case '3':  case '4':
+         case '5':  case '6':  case '7':  case '8':  case '9':
+           error ("matching constraint not valid in output operand");
+           break;
+
+         case 'p':  case 'g':  case 'r':
          default:
            allows_reg = 1;
            break;
@@ -1564,13 +1579,20 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
 #endif
            break;
 
-         case 'p':  case 'g':  case 'r':
            /* Whether or not a numeric constraint allows a register is
               decided by the matching constraint, and so there is no need
               to do anything special with them.  We must handle them in
               the default case, so that we don't unnecessarily force
               operands to memory.  */
          case '0':  case '1':  case '2':  case '3':  case '4':
+         case '5':  case '6':  case '7':  case '8':  case '9':
+           if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j]
+               >= '0' + noutputs)
+             error ("matching constraint references invalid operand number");
+
+           /* ... fall through ... */
+
+         case 'p':  case 'g':  case 'r':
          default:
            allows_reg = 1;
            break;
@@ -2717,14 +2739,14 @@ expand_return (retval)
       tree expr;
 
       do_jump (TREE_OPERAND (retval_rhs, 0), label, NULL_RTX);
-      expr = build (MODIFY_EXPR, TREE_TYPE (current_function_decl),
+      expr = build (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (current_function_decl)),
                    DECL_RESULT (current_function_decl),
                    TREE_OPERAND (retval_rhs, 1));
       TREE_SIDE_EFFECTS (expr) = 1;
       expand_return (expr);
       emit_label (label);
 
-      expr = build (MODIFY_EXPR, TREE_TYPE (current_function_decl),
+      expr = build (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (current_function_decl)),
                    DECL_RESULT (current_function_decl),
                    TREE_OPERAND (retval_rhs, 2));
       TREE_SIDE_EFFECTS (expr) = 1;
@@ -2912,9 +2934,7 @@ expand_return (retval)
        result_reg_mode = tmpmode;
       result_reg = gen_reg_rtx (result_reg_mode);
 
-      /* Now that the value is in pseudos, copy it to the result reg(s).  */
       emit_queue ();
-      free_temp_slots ();
       for (i = 0; i < n_regs; i++)
        emit_move_insn (operand_subword (result_reg, i, 0, result_reg_mode),
                        result_pseudos[i]);
@@ -2930,10 +2950,10 @@ expand_return (retval)
       && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG)
     {
       /* Calculate the return value into a pseudo reg.  */
-      val = expand_expr (retval_rhs, NULL_RTX, VOIDmode, 0);
+      val = gen_reg_rtx (DECL_MODE (DECL_RESULT (current_function_decl)));
+      val = expand_expr (retval_rhs, val, GET_MODE (val), 0);
+      val = force_not_mem (val);
       emit_queue ();
-      /* All temporaries have now been used.  */
-      free_temp_slots ();
       /* Return the calculated value, doing cleanups first.  */
       expand_value_return (val);
     }
@@ -2943,7 +2963,6 @@ expand_return (retval)
         calculate value into hard return reg.  */
       expand_expr (retval, const0_rtx, VOIDmode, 0);
       emit_queue ();
-      free_temp_slots ();
       expand_value_return (DECL_RTL (DECL_RESULT (current_function_decl)));
     }
 }
@@ -3050,22 +3069,13 @@ expand_start_bindings (exit_flag)
   thisblock->data.block.stack_level = 0;
   thisblock->data.block.cleanups = 0;
   thisblock->data.block.function_call_count = 0;
-#if 0
-  if (block_stack)
-    {
-      if (block_stack->data.block.cleanups == NULL_TREE
-         && (block_stack->data.block.outer_cleanups == NULL_TREE
-             || block_stack->data.block.outer_cleanups == empty_cleanup_list))
-       thisblock->data.block.outer_cleanups = empty_cleanup_list;
-      else
-       thisblock->data.block.outer_cleanups
-         = tree_cons (NULL_TREE, block_stack->data.block.cleanups,
-                      block_stack->data.block.outer_cleanups);
-    }
-  else
-    thisblock->data.block.outer_cleanups = 0;
-#endif
-#if 1
+  thisblock->data.block.exception_region = 0;
+  thisblock->data.block.target_temp_slot_level = target_temp_slot_level;
+
+  thisblock->data.block.conditional_code = 0;
+  thisblock->data.block.last_unconditional_cleanup = note;
+  thisblock->data.block.cleanup_ptr = &thisblock->data.block.cleanups;
+
   if (block_stack
       && !(block_stack->data.block.cleanups == NULL_TREE
           && block_stack->data.block.outer_cleanups == NULL_TREE))
@@ -3074,7 +3084,6 @@ expand_start_bindings (exit_flag)
                   block_stack->data.block.outer_cleanups);
   else
     thisblock->data.block.outer_cleanups = 0;
-#endif
   thisblock->data.block.label_chain = 0;
   thisblock->data.block.innermost_stack_block = stack_block_stack;
   thisblock->data.block.first_insn = note;
@@ -3090,6 +3099,91 @@ expand_start_bindings (exit_flag)
     }
 }
 
+/* Specify the scope of temporaries created by TARGET_EXPRs.  Similar
+   to CLEANUP_POINT_EXPR, but handles cases when a series of calls to
+   expand_expr are made.  After we end the region, we know that all
+   space for all temporaries that were created by TARGET_EXPRs will be
+   destroyed and their space freed for reuse.  */
+
+void
+expand_start_target_temps ()
+{
+  /* This is so that even if the result is preserved, the space
+     allocated will be freed, as we know that it is no longer in use.  */
+  push_temp_slots ();
+
+  /* Start a new binding layer that will keep track of all cleanup
+     actions to be performed.  */
+  expand_start_bindings (0);
+
+  target_temp_slot_level = temp_slot_level;
+}
+
+void
+expand_end_target_temps ()
+{
+  expand_end_bindings (NULL_TREE, 0, 0);
+  
+  /* This is so that even if the result is preserved, the space
+     allocated will be freed, as we know that it is no longer in use.  */
+  pop_temp_slots ();
+}
+
+/* Mark top block of block_stack as an implicit binding for an
+   exception region.  This is used to prevent infinite recursion when
+   ending a binding with expand_end_bindings.  It is only ever called
+   by expand_eh_region_start, as that it the only way to create a
+   block stack for a exception region.  */
+
+void
+mark_block_as_eh_region ()
+{
+  block_stack->data.block.exception_region = 1;
+  if (block_stack->next
+      && block_stack->next->data.block.conditional_code)
+    {
+      block_stack->data.block.conditional_code
+       = block_stack->next->data.block.conditional_code;
+      block_stack->data.block.last_unconditional_cleanup
+       = block_stack->next->data.block.last_unconditional_cleanup;
+      block_stack->data.block.cleanup_ptr
+       = block_stack->next->data.block.cleanup_ptr;
+    }
+}
+
+/* True if we are currently emitting insns in an area of output code
+   that is controlled by a conditional expression.  This is used by
+   the cleanup handling code to generate conditional cleanup actions.  */
+
+int
+conditional_context ()
+{
+  return block_stack && block_stack->data.block.conditional_code;
+}
+
+/* Mark top block of block_stack as not for an implicit binding for an
+   exception region.  This is only ever done by expand_eh_region_end
+   to let expand_end_bindings know that it is being called explicitly
+   to end the binding layer for just the binding layer associated with
+   the exception region, otherwise expand_end_bindings would try and
+   end all implicit binding layers for exceptions regions, and then
+   one normal binding layer.  */
+
+void
+mark_block_as_not_eh_region ()
+{
+  block_stack->data.block.exception_region = 0;
+}
+
+/* True if the top block of block_stack was marked as for an exception
+   region by mark_block_as_eh_region.  */
+
+int
+is_eh_region ()
+{
+  return block_stack && block_stack->data.block.exception_region;
+}
+
 /* Given a pointer to a BLOCK node, save a pointer to the most recently
    generated NOTE_INSN_BLOCK_END in the BLOCK_END_NOTE field of the given
    BLOCK node.  */
@@ -3117,15 +3211,34 @@ expand_end_bindings (vars, mark_ends, dont_jump_in)
      int mark_ends;
      int dont_jump_in;
 {
-  register struct nesting *thisblock = block_stack;
+  register struct nesting *thisblock;
   register tree decl;
 
+  while (block_stack->data.block.exception_region)
+    {
+      /* Because we don't need or want a new temporary level and
+        because we didn't create one in expand_eh_region_start,
+        create a fake one now to avoid removing one in
+        expand_end_bindings.  */
+      push_temp_slots ();
+
+      block_stack->data.block.exception_region = 0;
+
+      expand_end_bindings (NULL_TREE, 0, 0);
+    }
+
   if (output_bytecode)
     {
       bc_expand_end_bindings (vars, mark_ends, dont_jump_in);
       return;
     }
 
+  /* Since expand_eh_region_start does an expand_start_bindings, we
+     have to first end all the bindings that were created by
+     expand_eh_region_start.  */
+     
+  thisblock = block_stack;
+
   if (warn_unused)
     for (decl = vars; decl; decl = TREE_CHAIN (decl))
       if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL
@@ -3351,6 +3464,9 @@ expand_end_bindings (vars, mark_ends, dont_jump_in)
          use_variable (rtl);
       }
 
+  /* Restore the temporary level of TARGET_EXPRs.  */
+  target_temp_slot_level = thisblock->data.block.target_temp_slot_level;
+
   /* Restore block_stack level for containing block.  */
 
   stack_block_stack = thisblock->data.block.innermost_stack_block;
@@ -3456,6 +3572,7 @@ expand_decl (decl)
                          (TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl)))
                           / BITS_PER_UNIT));
     }
+
   else if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST)
     {
       /* Variable of fixed size that goes on the stack.  */
@@ -3602,7 +3719,7 @@ bc_expand_decl (decl, cleanup)
   else if (DECL_SIZE (decl) == 0)
 
     /* Variable with incomplete type.  The stack offset herein will be
-       fixed later in expand_decl_init ().  */
+       fixed later in expand_decl_init.  */
     DECL_RTL (decl) = bc_gen_rtx ((char *) 0, 0, (struct bc_label *) 0);
 
   else if (TREE_CONSTANT (DECL_SIZE (decl)))
@@ -3753,7 +3870,8 @@ bc_expand_decl_init (decl)
 
    We wrap CLEANUP in an UNSAVE_EXPR node, so that we can expand the
    CLEANUP multiple times, and have the correct semantics.  This
-   happens in exception handling, and for non-local gotos.
+   happens in exception handling, for gotos, returns, breaks that
+   leave the current scope.
 
    If CLEANUP is nonzero and DECL is zero, we record a cleanup
    that is not associated with any particular variable.   */
@@ -3772,16 +3890,168 @@ expand_decl_cleanup (decl, cleanup)
 
   if (cleanup != 0)
     {
+      tree t;
+      rtx seq;
+      tree *cleanups = &thisblock->data.block.cleanups;
+      int cond_context = conditional_context ();
+
+      if (cond_context)
+       {
+         rtx flag = gen_reg_rtx (word_mode);
+         rtx set_flag_0;
+         tree cond;
+
+         start_sequence ();
+         emit_move_insn (flag, const0_rtx);
+         set_flag_0 = get_insns ();
+         end_sequence ();
+
+         thisblock->data.block.last_unconditional_cleanup
+           = emit_insns_after (set_flag_0,
+                               thisblock->data.block.last_unconditional_cleanup);
+
+         emit_move_insn (flag, const1_rtx);
+
+         /* All cleanups must be on the function_obstack.  */
+         push_obstacks_nochange ();
+         resume_temporary_allocation ();
+
+         cond = build_decl (VAR_DECL, NULL_TREE, type_for_mode (word_mode, 1));
+         DECL_RTL (cond) = flag;
+
+         /* Conditionalize the cleanup.  */
+         cleanup = build (COND_EXPR, void_type_node,
+                          truthvalue_conversion (cond),
+                          cleanup, integer_zero_node);
+         cleanup = fold (cleanup);
+
+         pop_obstacks ();
+
+         cleanups = thisblock->data.block.cleanup_ptr;
+       }
+
+      /* All cleanups must be on the function_obstack.  */
+      push_obstacks_nochange ();
+      resume_temporary_allocation ();
       cleanup = unsave_expr (cleanup);
+      pop_obstacks ();
 
-      thisblock->data.block.cleanups
-       = temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups);
-      /* If this block has a cleanup, it belongs in stack_block_stack.  */
-      stack_block_stack = thisblock;
-      expand_eh_region_start ();
+      t = *cleanups = temp_tree_cons (decl, cleanup, *cleanups);
+
+      if (! cond_context)
+       /* If this block has a cleanup, it belongs in stack_block_stack.  */
+       stack_block_stack = thisblock;
+
+      if (cond_context)
+       {
+         start_sequence ();
+       }
+
+      /* If this was optimized so that there is no exception region for the
+        cleanup, then mark the TREE_LIST node, so that we can later tell
+        if we need to call expand_eh_region_end.  */
+      if (expand_eh_region_start_tree (decl, cleanup))
+       TREE_ADDRESSABLE (t) = 1;
+
+      if (cond_context)
+       {
+         seq = get_insns ();
+         end_sequence ();
+         thisblock->data.block.last_unconditional_cleanup
+           = emit_insns_after (seq,
+                               thisblock->data.block.last_unconditional_cleanup);
+       }
+      else
+       {
+         thisblock->data.block.last_unconditional_cleanup
+           = get_last_insn ();
+         thisblock->data.block.cleanup_ptr = &thisblock->data.block.cleanups;
+       }
     }
   return 1;
 }
+
+/* Arrange for the top element of the dynamic cleanup chain to be
+   popped if we exit the current binding contour.  DECL is the
+   associated declaration, if any, otherwise NULL_TREE.  If the
+   current contour is left via an exception, then __sjthrow will pop
+   the top element off the dynamic cleanup chain.  The code that
+   avoids doing the action we push into the cleanup chain in the
+   exceptional case is contained in expand_cleanups.
+
+   This routine is only used by expand_eh_region_start, and that is
+   the only way in which an exception region should be started.  This
+   routine is only used when using the setjmp/longjmp codegen method
+   for exception handling.  */
+
+int
+expand_dcc_cleanup (decl)
+     tree decl;
+{
+  struct nesting *thisblock = block_stack;
+  tree cleanup;
+
+  /* Error if we are not in any block.  */
+  if (thisblock == 0)
+    return 0;
+
+  /* Record the cleanup for the dynamic handler chain.  */
+
+  /* All cleanups must be on the function_obstack.  */
+  push_obstacks_nochange ();
+  resume_temporary_allocation ();
+  cleanup = make_node (POPDCC_EXPR);
+  pop_obstacks ();
+
+  /* Add the cleanup in a manner similar to expand_decl_cleanup.  */
+  thisblock->data.block.cleanups
+    = temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups);
+
+  /* If this block has a cleanup, it belongs in stack_block_stack.  */
+  stack_block_stack = thisblock;
+  return 1;
+}
+
+/* Arrange for the top element of the dynamic handler chain to be
+   popped if we exit the current binding contour.  DECL is the
+   assciated declaration, if any, otherwise NULL_TREE.  If the current
+   contour is left via an exception, then __sjthrow will pop the top
+   element off the dynamic handler chain.  The code that avoids doing
+   the action we push into the handler chain in the exceptional case
+   is contained in expand_cleanups.
+
+   This routine is only used by expand_eh_region_start, and that is
+   the only way in which an exception region should be started.  This
+   routine is only used when using the setjmp/longjmp codegen method
+   for exception handling.  */
+
+int
+expand_dhc_cleanup (decl)
+     tree decl;
+{
+  struct nesting *thisblock = block_stack;
+  tree cleanup;
+
+  /* Error if we are not in any block.  */
+  if (thisblock == 0)
+    return 0;
+
+  /* Record the cleanup for the dynamic handler chain.  */
+
+  /* All cleanups must be on the function_obstack.  */
+  push_obstacks_nochange ();
+  resume_temporary_allocation ();
+  cleanup = make_node (POPDHC_EXPR);
+  pop_obstacks ();
+
+  /* Add the cleanup in a manner similar to expand_decl_cleanup.  */
+  thisblock->data.block.cleanups
+    = temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups);
+
+  /* If this block has a cleanup, it belongs in stack_block_stack.  */
+  stack_block_stack = thisblock;
+  return 1;
+}
 \f
 /* DECL is an anonymous union.  CLEANUP is a cleanup for DECL.
    DECL_ELTS is the list of elements that belong to DECL's type.
@@ -3879,7 +4149,19 @@ expand_cleanups (list, dont_do, in_fixup, reachable)
        else
          {
            if (! in_fixup)
-             expand_eh_region_end (TREE_VALUE (tail));
+             {
+               tree cleanup = TREE_VALUE (tail);
+
+               /* See expand_d{h,c}c_cleanup for why we avoid this.  */
+               if (TREE_CODE (cleanup) != POPDHC_EXPR
+                   && TREE_CODE (cleanup) != POPDCC_EXPR
+                   /* See expand_eh_region_start_tree for this case.  */
+                   && ! TREE_ADDRESSABLE (tail))
+                 {
+                   cleanup = protect_with_terminate (cleanup);
+                   expand_eh_region_end (cleanup);
+                 }
+             }
 
            if (reachable)
              {
@@ -3898,6 +4180,29 @@ expand_cleanups (list, dont_do, in_fixup, reachable)
       }
 }
 
+/* Mark when the context we are emitting RTL for as a conditional
+   context, so that any cleanup actions we register with
+   expand_decl_init will be properly conditionalized when those
+   cleanup actions are later performed.  Must be called before any
+   expression (tree) is expanded that is within a contidional context.  */
+
+void
+start_cleanup_deferal ()
+{
+  ++block_stack->data.block.conditional_code;
+}
+
+/* Mark the end of a conditional region of code.  Because cleanup
+   deferals may be nested, we may still be in a conditional region
+   after we end the currently deferred cleanups, only after we end all
+   deferred cleanups, are we back in unconditional code.  */
+
+void
+end_cleanup_deferal ()
+{
+  --block_stack->data.block.conditional_code;
+}
+
 /* Move all cleanups from the current block_stack
    to the containing block_stack, where they are assumed to
    have been created.  If anything can cause a temporary to
@@ -3941,11 +4246,7 @@ any_pending_cleanups (this_contour)
   if (this_contour && block_stack->data.block.cleanups != NULL)
     return 1;
   if (block_stack->data.block.cleanups == 0
-      && (block_stack->data.block.outer_cleanups == 0
-#if 0
-         || block_stack->data.block.outer_cleanups == empty_cleanup_list
-#endif
-         ))
+      && block_stack->data.block.outer_cleanups == 0)
     return 0;
 
   for (block = block_stack->next; block; block = block->next)
@@ -4006,6 +4307,8 @@ expand_start_case (exit_flag, expr, type, printname)
     emit_note (NULL_PTR, NOTE_INSN_DELETED);
 
   thiscase->data.case_stmt.start = get_last_insn ();
+
+  start_cleanup_deferal ();
 }
 
 
@@ -4058,6 +4361,7 @@ expand_start_case_dummy ()
   thiscase->data.case_stmt.num_ranges = 0;
   case_stack = thiscase;
   nesting_stack = thiscase;
+  start_cleanup_deferal ();
 }
 
 /* End a dummy case statement.  */
@@ -4065,6 +4369,7 @@ expand_start_case_dummy ()
 void
 expand_end_case_dummy ()
 {
+  end_cleanup_deferal ();
   POPSTACK (case_stack);
 }
 
@@ -4089,7 +4394,7 @@ case_index_expr_type ()
    If VALUE is a duplicate or overlaps, return 2 and do nothing
    except store the (first) duplicate node in *DUPLICATE.
    If VALUE is out of range, return 3 and do nothing.
-   If we are jumping into the scope of a cleaup or var-sized array, return 5.
+   If we are jumping into the scope of a cleanup or var-sized array, return 5.
    Return 0 on success.
 
    Extended to handle range statements.  */
@@ -4193,6 +4498,10 @@ pushcase_range (value1, value2, converter, label, duplicate)
   if (! (case_stack && case_stack->data.case_stmt.start))
     return 1;
 
+  /* Fail if the range is empty.  */
+  if (tree_int_cst_lt (value2, value1))
+    return 4;
+
   if (stack_block_stack
       && stack_block_stack->depth > case_stack->depth)
     return 5;
@@ -4227,11 +4536,11 @@ pushcase_range (value1, value2, converter, label, duplicate)
 
   /* Convert VALUEs to type in which the comparisons are nominally done.  */
   if (value1 == 0)  /* Negative infinity.  */
-    value1 = TYPE_MIN_VALUE(index_type);
+    value1 = TYPE_MIN_VALUE (index_type);
   value1 = (*converter) (nominal_type, value1);
 
   if (value2 == 0)  /* Positive infinity.  */
-    value2 = TYPE_MAX_VALUE(index_type);
+    value2 = TYPE_MAX_VALUE (index_type);
   value2 = (*converter) (nominal_type, value2);
 
   /* Fail if these values are out of range.  */
@@ -4241,10 +4550,6 @@ pushcase_range (value1, value2, converter, label, duplicate)
   if (! int_fits_type_p (value2, index_type))
     return 3;
 
-  /* Fail if the range is empty.  */
-  if (tree_int_cst_lt (value2, value1))
-    return 4;
-
   return add_case_node (value1, value2, label, duplicate);
 }
 
@@ -5058,6 +5363,8 @@ expand_end_case (orig_index)
       if (count != 0)
        range = fold (build (MINUS_EXPR, index_type, maxval, minval));
 
+      end_cleanup_deferal ();
+
       if (count == 0)
        {
          expand_expr (index_expr, const0_rtx, VOIDmode, 0);
@@ -5325,6 +5632,8 @@ expand_end_case (orig_index)
       reorder_insns (before_case, get_last_insn (),
                     thiscase->data.case_stmt.start);
     }
+  else
+    end_cleanup_deferal ();
 
   if (thiscase->exit_label)
     emit_label (thiscase->exit_label);
@@ -5578,12 +5887,18 @@ group_case_nodes (head)
   while (node)
     {
       rtx lb = next_real_insn (label_rtx (node->code_label));
+      rtx lb2;
       case_node_ptr np = node;
 
       /* Try to group the successors of NODE with NODE.  */
       while (((np = np->right) != 0)
             /* Do they jump to the same place?  */
-            && next_real_insn (label_rtx (np->code_label)) == lb
+            && ((lb2 = next_real_insn (label_rtx (np->code_label))) == lb
+                || (lb != 0 && lb2 != 0
+                    && simplejump_p (lb)
+                    && simplejump_p (lb2)
+                    && rtx_equal_p (SET_SRC (PATTERN (lb)),
+                                    SET_SRC (PATTERN (lb2)))))
             /* Are their ranges consecutive?  */
             && tree_int_cst_equal (np->low,
                                    fold (build (PLUS_EXPR,
@@ -6168,4 +6483,3 @@ unroll_block_trees ()
 
   reorder_blocks (block_vector, block, get_insns ());
 }
-