Patchwork Cleanup tree-switch-conversion a bit

login
register
mail settings
Submitter Steven Bosscher
Date April 17, 2012, 10:04 p.m.
Message ID <CABu31nM3YtFCmGnmb5pfWM7+Ugf2UoyBLWGKFFCb7C=kW_=aFg@mail.gmail.com>
Download mbox | patch
Permalink /patch/153327/
State New
Headers show

Comments

Steven Bosscher - April 17, 2012, 10:04 p.m.
Hello,

This is another step towards moving GIMPLE_SWITCH expansion to an
earlier point in the pipeline.

With the attached patch, some of the logic from stmt.c:add_case_node()
is moved to gimplify.c:gimplify_switch_expr(). This includes:

* Code to drop case labels that are out of range for the switch index
expression. (Actually, I suspect this code hasn't worked properly
since gimplification was introduced, because the switch index
expression can be promoted by language specific gimplification, so
expand_case never actually sees the proper type with the current
implementation in stmt.c.)

* Code to fold_convert case label values to the right type. I've opted
to go for folding to the original type of the SWITCH_EXPR, rather than
to the post-gimplification switch index type.

* Code to canonicalize CASE_LABEL's subnodes, CASE_LOW and CASE_HIGH.
I've chosen to impose strict requirements that CASE_HIGH > CASE_LOW if
CASE_HIGH is non-zero. This is different from what add_case_node does,
but I think it makes sense to go for the minimal representation here:
The case labels in stmt.c never lived very long (only during expand)
but GIMPLE_SWITCH statements stay around for much of the compilation
process and can also be streamed out, etc.

Bootstrapped and tested on powerpc-unknown-linux-gnu. OK for trunk?

Ciao!
Steven
* targhooks.c (default_case_values_threshold): Fix code style nit.

	* stmt.c (add_case_node, expand_case): Move logic to remove/reduce
	case range and type folding from here...
	* gimplify.c (gimplify_switch_expr): ... to here.
Richard Guenther - April 18, 2012, 9:10 a.m.
On Wed, 18 Apr 2012, Steven Bosscher wrote:

> Hello,
> 
> This is another step towards moving GIMPLE_SWITCH expansion to an
> earlier point in the pipeline.
> 
> With the attached patch, some of the logic from stmt.c:add_case_node()
> is moved to gimplify.c:gimplify_switch_expr(). This includes:
> 
> * Code to drop case labels that are out of range for the switch index
> expression. (Actually, I suspect this code hasn't worked properly
> since gimplification was introduced, because the switch index
> expression can be promoted by language specific gimplification, so
> expand_case never actually sees the proper type with the current
> implementation in stmt.c.)
> 
> * Code to fold_convert case label values to the right type. I've opted
> to go for folding to the original type of the SWITCH_EXPR, rather than
> to the post-gimplification switch index type.
> 
> * Code to canonicalize CASE_LABEL's subnodes, CASE_LOW and CASE_HIGH.
> I've chosen to impose strict requirements that CASE_HIGH > CASE_LOW if
> CASE_HIGH is non-zero. This is different from what add_case_node does,
> but I think it makes sense to go for the minimal representation here:
> The case labels in stmt.c never lived very long (only during expand)
> but GIMPLE_SWITCH statements stay around for much of the compilation
> process and can also be streamed out, etc.
> 
> Bootstrapped and tested on powerpc-unknown-linux-gnu. OK for trunk?

I wonder about

@@ -1575,6 +1575,9 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pr
   tree switch_expr = *expr_p;
   gimple_seq switch_body_seq = NULL;
   enum gimplify_status ret;
+  tree index_type = TREE_TYPE (switch_expr);
+  if (index_type == void_type_node)
+    index_type = TREE_TYPE (SWITCH_COND (switch_expr));

what frontends use void_type_node here contrary to the docs in tree.def:

   TREE_TYPE is the original type of the condition, before any
   language required type conversions.  It may be NULL, in which case
   the original type and final types are assumed to be the same.

which says you'd need to handle NULL instead?  Thus, can you either
adjust the docs in tree.def or the frontends?

Ok with either change.

Thanks,
Richard.
Steven Bosscher - April 18, 2012, 10:57 a.m.
On Wed, Apr 18, 2012 at 11:10 AM, Richard Guenther <rguenther@suse.de> wrote:
> I wonder about
>
> @@ -1575,6 +1575,9 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pr
>   tree switch_expr = *expr_p;
>   gimple_seq switch_body_seq = NULL;
>   enum gimplify_status ret;
> +  tree index_type = TREE_TYPE (switch_expr);
> +  if (index_type == void_type_node)
> +    index_type = TREE_TYPE (SWITCH_COND (switch_expr));
>
> what frontends use void_type_node here contrary to the docs in tree.def:
>
>   TREE_TYPE is the original type of the condition, before any
>   language required type conversions.  It may be NULL, in which case
>   the original type and final types are assumed to be the same.
>
> which says you'd need to handle NULL instead?  Thus, can you either
> adjust the docs in tree.def or the frontends?

That code was copied from stmt.c, and I was surprised by it, too.

The Fortran frond end builds SWITCH_EXPRs with build3_v, i.e.
void_type_node-typed. The Go front ends also build SWITCH_EXPRs with
void_type_node. The C-family and Java front ends build SWITCH_EXPRs
with a non-void type.

But from gimplify.c:is_gimple_stmt():

    case SWITCH_EXPR:
(...)
      /* These are always void.  */
      return true;

See these files for all the locations I could find where a SWITCH_EXPR is built:
java/expr.c:  switch_expr = build3 (SWITCH_EXPR, TREE_TYPE (selector), selector,
go/go-gcc.cc:  tree t = build3_loc(switch_location.gcc_location(), SWITCH_EXPR,
cp/cp-gimplify.c:  t = build3 (SWITCH_EXPR, SWITCH_STMT_TYPE (stmt),
c-typeck.c:  cs->switch_expr = build3 (SWITCH_EXPR, orig_type, exp,
NULL_TREE, NULL_TREE);
fortran/trans-stmt.c:  tmp = build3_v (SWITCH_EXPR, se.expr, tmp, NULL_TREE);
fortran/trans-stmt.c:     tmp = build3_v (SWITCH_EXPR, case_num, tmp,
NULL_TREE);
fortran/trans-stmt.c:  tmp = build3_v (SWITCH_EXPR, case_num, tmp, NULL_TREE);
fortran/trans-io.c:  tmp = build3_v (SWITCH_EXPR, rc, tmp, NULL_TREE);
fortran/trans-decl.c:  tmp = build3_v (SWITCH_EXPR, val, tmp, NULL_TREE);
ada/gcc-interface/trans.c:  gnu_result = build3 (SWITCH_EXPR,
TREE_TYPE (gnu_expr), gnu_expr,

Perhaps the Fortran and Go front ends should build SWITCH_EXPRs with a
NULL type, and gimplify_switch_expr() should assert that the
SWITCH_EXPR type is never void_type node. That would make things match
the documentation in tree.def again. I'll test a patch with these
changes and commit it if it works.

Ciao!
Steven
Richard Guenther - April 18, 2012, 11:23 a.m.
On Wed, 18 Apr 2012, Steven Bosscher wrote:

> On Wed, Apr 18, 2012 at 11:10 AM, Richard Guenther <rguenther@suse.de> wrote:
> > I wonder about
> >
> > @@ -1575,6 +1575,9 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pr
> >   tree switch_expr = *expr_p;
> >   gimple_seq switch_body_seq = NULL;
> >   enum gimplify_status ret;
> > +  tree index_type = TREE_TYPE (switch_expr);
> > +  if (index_type == void_type_node)
> > +    index_type = TREE_TYPE (SWITCH_COND (switch_expr));
> >
> > what frontends use void_type_node here contrary to the docs in tree.def:
> >
> >   TREE_TYPE is the original type of the condition, before any
> >   language required type conversions.  It may be NULL, in which case
> >   the original type and final types are assumed to be the same.
> >
> > which says you'd need to handle NULL instead?  Thus, can you either
> > adjust the docs in tree.def or the frontends?
> 
> That code was copied from stmt.c, and I was surprised by it, too.
> 
> The Fortran frond end builds SWITCH_EXPRs with build3_v, i.e.
> void_type_node-typed. The Go front ends also build SWITCH_EXPRs with
> void_type_node. The C-family and Java front ends build SWITCH_EXPRs
> with a non-void type.
> 
> But from gimplify.c:is_gimple_stmt():
> 
>     case SWITCH_EXPR:
> (...)
>       /* These are always void.  */
>       return true;
> 
> See these files for all the locations I could find where a SWITCH_EXPR is built:
> java/expr.c:  switch_expr = build3 (SWITCH_EXPR, TREE_TYPE (selector), selector,
> go/go-gcc.cc:  tree t = build3_loc(switch_location.gcc_location(), SWITCH_EXPR,
> cp/cp-gimplify.c:  t = build3 (SWITCH_EXPR, SWITCH_STMT_TYPE (stmt),
> c-typeck.c:  cs->switch_expr = build3 (SWITCH_EXPR, orig_type, exp,
> NULL_TREE, NULL_TREE);
> fortran/trans-stmt.c:  tmp = build3_v (SWITCH_EXPR, se.expr, tmp, NULL_TREE);
> fortran/trans-stmt.c:     tmp = build3_v (SWITCH_EXPR, case_num, tmp,
> NULL_TREE);
> fortran/trans-stmt.c:  tmp = build3_v (SWITCH_EXPR, case_num, tmp, NULL_TREE);
> fortran/trans-io.c:  tmp = build3_v (SWITCH_EXPR, rc, tmp, NULL_TREE);
> fortran/trans-decl.c:  tmp = build3_v (SWITCH_EXPR, val, tmp, NULL_TREE);
> ada/gcc-interface/trans.c:  gnu_result = build3 (SWITCH_EXPR,
> TREE_TYPE (gnu_expr), gnu_expr,
> 
> Perhaps the Fortran and Go front ends should build SWITCH_EXPRs with a
> NULL type, and gimplify_switch_expr() should assert that the
> SWITCH_EXPR type is never void_type node. That would make things match
> the documentation in tree.def again. I'll test a patch with these
> changes and commit it if it works.

I suppose generic tree handling routines are confused by NULL TREE_TYPE
and thus changing the docs to void_type_node would be more appropriate.

Richard.
Steven Bosscher - April 18, 2012, 3:17 p.m.
On Wed, Apr 18, 2012 at 1:23 PM, Richard Guenther <rguenther@suse.de> wrote:
> I suppose generic tree handling routines are confused by NULL TREE_TYPE
> and thus changing the docs to void_type_node would be more appropriate.

I don't agree with that. The documented behavior is much older than
either the Fortran or the go front end, so the safest way is to fix
those front ends to match the documentation.

With my patch modified as attached, bootstrap&test still passes. OK?

Ciao!
Steven
Steven Bosscher - April 18, 2012, 6:30 p.m.
On Wed, Apr 18, 2012 at 5:17 PM, Steven Bosscher <stevenb.gcc@gmail.com> wrote:
> On Wed, Apr 18, 2012 at 1:23 PM, Richard Guenther <rguenther@suse.de> wrote:
>> I suppose generic tree handling routines are confused by NULL TREE_TYPE
>> and thus changing the docs to void_type_node would be more appropriate.
>
> I don't agree with that. The documented behavior is much older than
> either the Fortran or the go front end, so the safest way is to fix
> those front ends to match the documentation.
>
> With my patch modified as attached, bootstrap&test still passes. OK?

The Go bits approved on IRC by Iant, the Fortran bits are obvious, and
the rest was already approved. This is r186579 now.

Ciao!
Steven

Patch

Index: targhooks.c
===================================================================
--- targhooks.c	(revision 186526)
+++ targhooks.c	(working copy)
@@ -1200,7 +1200,8 @@  default_target_can_inline_p (tree caller, tree cal
    this means extra overhead for dispatch tables, which raises the
    threshold for using them.  */
 
-unsigned int default_case_values_threshold (void)
+unsigned int
+default_case_values_threshold (void)
 {
   return (HAVE_casesi ? 4 : 5);
 }
Index: tree-switch-conversion.c
===================================================================
--- tree-switch-conversion.c	(revision 186526)
+++ tree-switch-conversion.c	(working copy)
@@ -24,8 +24,8 @@  Software Foundation, 51 Franklin Street, Fifth Flo
      Switch initialization conversion
 
 The following pass changes simple initializations of scalars in a switch
-statement into initializations from a static array.  Obviously, the values must
-be constant and known at compile time and a default branch must be
+statement into initializations from a static array.  Obviously, the values
+must be constant and known at compile time and a default branch must be
 provided.  For example, the following code:
 
         int a,b;
@@ -162,16 +162,12 @@  struct switch_conv_info
   basic_block bit_test_bb[2];
 };
 
-/* Global pass info.  */
-static struct switch_conv_info info;
-
-
 /* Checks whether the range given by individual case statements of the SWTCH
    switch statement isn't too big and whether the number of branches actually
    satisfies the size of the new array.  */
 
 static bool
-check_range (gimple swtch)
+check_range (gimple swtch, struct switch_conv_info *info)
 {
   tree min_case, max_case;
   unsigned int branch_num = gimple_switch_num_labels (swtch);
@@ -181,7 +177,7 @@  static bool
      is a default label which is the first in the vector.  */
 
   min_case = gimple_switch_label (swtch, 1);
-  info.range_min = CASE_LOW (min_case);
+  info->range_min = CASE_LOW (min_case);
 
   gcc_assert (branch_num > 1);
   gcc_assert (CASE_LOW (gimple_switch_label (swtch, 0)) == NULL_TREE);
@@ -191,22 +187,22 @@  static bool
   else
     range_max = CASE_LOW (max_case);
 
-  gcc_assert (info.range_min);
+  gcc_assert (info->range_min);
   gcc_assert (range_max);
 
-  info.range_size = int_const_binop (MINUS_EXPR, range_max, info.range_min);
+  info->range_size = int_const_binop (MINUS_EXPR, range_max, info->range_min);
 
-  gcc_assert (info.range_size);
-  if (!host_integerp (info.range_size, 1))
+  gcc_assert (info->range_size);
+  if (!host_integerp (info->range_size, 1))
     {
-      info.reason = "index range way too large or otherwise unusable.\n";
+      info->reason = "index range way too large or otherwise unusable";
       return false;
     }
 
-  if ((unsigned HOST_WIDE_INT) tree_low_cst (info.range_size, 1)
+  if ((unsigned HOST_WIDE_INT) tree_low_cst (info->range_size, 1)
       > ((unsigned) branch_num * SWITCH_CONVERSION_BRANCH_RATIO))
     {
-      info.reason = "the maximum range-branch ratio exceeded.\n";
+      info->reason = "the maximum range-branch ratio exceeded";
       return false;
     }
 
@@ -219,7 +215,7 @@  static bool
    and returns true.  Otherwise returns false.  */
 
 static bool
-check_process_case (tree cs)
+check_process_case (tree cs, struct switch_conv_info *info)
 {
   tree ldecl;
   basic_block label_bb, following_bb;
@@ -228,48 +224,48 @@  static bool
   ldecl = CASE_LABEL (cs);
   label_bb = label_to_block (ldecl);
 
-  e = find_edge (info.switch_bb, label_bb);
+  e = find_edge (info->switch_bb, label_bb);
   gcc_assert (e);
 
   if (CASE_LOW (cs) == NULL_TREE)
     {
       /* Default branch.  */
-      info.default_prob = e->probability;
-      info.default_count = e->count;
+      info->default_prob = e->probability;
+      info->default_count = e->count;
     }
   else
     {
       int i;
-      info.other_count += e->count;
+      info->other_count += e->count;
       for (i = 0; i < 2; i++)
-	if (info.bit_test_bb[i] == label_bb)
+	if (info->bit_test_bb[i] == label_bb)
 	  break;
-	else if (info.bit_test_bb[i] == NULL)
+	else if (info->bit_test_bb[i] == NULL)
 	  {
-	    info.bit_test_bb[i] = label_bb;
-	    info.bit_test_uniq++;
+	    info->bit_test_bb[i] = label_bb;
+	    info->bit_test_uniq++;
 	    break;
 	  }
       if (i == 2)
-	info.bit_test_uniq = 3;
+	info->bit_test_uniq = 3;
       if (CASE_HIGH (cs) != NULL_TREE
 	  && ! tree_int_cst_equal (CASE_LOW (cs), CASE_HIGH (cs)))
-	info.bit_test_count += 2;
+	info->bit_test_count += 2;
       else
-	info.bit_test_count++;
+	info->bit_test_count++;
     }
 
   if (!label_bb)
     {
-      info.reason = "  Bad case - cs BB  label is NULL\n";
+      info->reason = "bad case - cs BB  label is NULL";
       return false;
     }
 
   if (!single_pred_p (label_bb))
     {
-      if (info.final_bb && info.final_bb != label_bb)
+      if (info->final_bb && info->final_bb != label_bb)
 	{
-	  info.reason = "  Bad case - a non-final BB has two predecessors\n";
+	  info->reason = "bad case - a non-final BB has two predecessors";
 	  return false; /* sth complex going on in this branch  */
 	}
 
@@ -279,7 +275,7 @@  static bool
     {
       if (!empty_block_p (label_bb))
 	{
-	  info.reason = "  Bad case - a non-final BB not empty\n";
+	  info->reason = "bad case - a non-final BB not empty";
 	  return false;
 	}
 
@@ -287,11 +283,11 @@  static bool
       following_bb = single_succ (label_bb);
     }
 
-  if (!info.final_bb)
-    info.final_bb = following_bb;
-  else if (info.final_bb != following_bb)
+  if (!info->final_bb)
+    info->final_bb = following_bb;
+  else if (info->final_bb != following_bb)
     {
-      info.reason = "  Bad case - different final BB\n";
+      info->reason = "bad case - different final BB";
       return false; /* the only successor is not common for all the branches */
     }
 
@@ -304,31 +300,31 @@  static bool
    phi nodes are OK, otherwise false.  */
 
 static bool
-check_final_bb (void)
+check_final_bb (struct switch_conv_info *info)
 {
   gimple_stmt_iterator gsi;
 
-  info.phi_count = 0;
-  for (gsi = gsi_start_phis (info.final_bb); !gsi_end_p (gsi); gsi_next (&gsi))
+  info->phi_count = 0;
+  for (gsi = gsi_start_phis (info->final_bb); !gsi_end_p (gsi); gsi_next (&gsi))
     {
       gimple phi = gsi_stmt (gsi);
       unsigned int i;
 
-      info.phi_count++;
+      info->phi_count++;
 
       for (i = 0; i < gimple_phi_num_args (phi); i++)
 	{
 	  basic_block bb = gimple_phi_arg_edge (phi, i)->src;
 
-	  if (bb == info.switch_bb
-	      || (single_pred_p (bb) && single_pred (bb) == info.switch_bb))
+	  if (bb == info->switch_bb
+	      || (single_pred_p (bb) && single_pred (bb) == info->switch_bb))
 	    {
 	      tree reloc, val;
 
 	      val = gimple_phi_arg_def (phi, i);
 	      if (!is_gimple_ip_invariant (val))
 		{
-		  info.reason = "   Non-invariant value from a case\n";
+		  info->reason = "non-invariant value from a case";
 		  return false; /* Non-invariant argument.  */
 		}
 	      reloc = initializer_constant_valid_p (val, TREE_TYPE (val));
@@ -336,11 +332,11 @@  static bool
 		  || (!flag_pic && reloc == NULL_TREE))
 		{
 		  if (reloc)
-		    info.reason
-		      = "   Value from a case would need runtime relocations\n";
+		    info->reason
+		      = "value from a case would need runtime relocations";
 		  else
-		    info.reason
-		      = "   Value from a case is not a valid initializer\n";
+		    info->reason
+		      = "value from a case is not a valid initializer";
 		  return false;
 		}
 	    }
@@ -355,17 +351,17 @@  static bool
    vectors that will become constructors of new arrays.  */
 
 static void
-create_temp_arrays (void)
+create_temp_arrays (struct switch_conv_info *info)
 {
   int i;
 
-  info.default_values = XCNEWVEC (tree, info.phi_count * 3);
-  info.constructors = XCNEWVEC (VEC (constructor_elt, gc) *, info.phi_count);
-  info.target_inbound_names = info.default_values + info.phi_count;
-  info.target_outbound_names = info.target_inbound_names + info.phi_count;
-  for (i = 0; i < info.phi_count; i++)
-    info.constructors[i]
-      = VEC_alloc (constructor_elt, gc, tree_low_cst (info.range_size, 1) + 1);
+  info->default_values = XCNEWVEC (tree, info->phi_count * 3);
+  info->constructors = XCNEWVEC (VEC (constructor_elt, gc) *, info->phi_count);
+  info->target_inbound_names = info->default_values + info->phi_count;
+  info->target_outbound_names = info->target_inbound_names + info->phi_count;
+  for (i = 0; i < info->phi_count; i++)
+    info->constructors[i]
+      = VEC_alloc (constructor_elt, gc, tree_low_cst (info->range_size, 1) + 1);
 }
 
 /* Free the arrays created by create_temp_arrays().  The vectors that are
@@ -373,17 +369,17 @@  static void
    already become constructors and must be preserved.  */
 
 static void
-free_temp_arrays (void)
+free_temp_arrays (struct switch_conv_info *info)
 {
-  XDELETEVEC (info.constructors);
-  XDELETEVEC (info.default_values);
+  XDELETEVEC (info->constructors);
+  XDELETEVEC (info->default_values);
 }
 
 /* Populate the array of default values in the order of phi nodes.
    DEFAULT_CASE is the CASE_LABEL_EXPR for the default switch branch.  */
 
 static void
-gather_default_values (tree default_case)
+gather_default_values (tree default_case, struct switch_conv_info *info)
 {
   gimple_stmt_iterator gsi;
   basic_block bb = label_to_block (CASE_LABEL (default_case));
@@ -392,17 +388,17 @@  static void
 
   gcc_assert (CASE_LOW (default_case) == NULL_TREE);
 
-  if (bb == info.final_bb)
-    e = find_edge (info.switch_bb, bb);
+  if (bb == info->final_bb)
+    e = find_edge (info->switch_bb, bb);
   else
     e = single_succ_edge (bb);
 
-  for (gsi = gsi_start_phis (info.final_bb); !gsi_end_p (gsi); gsi_next (&gsi))
+  for (gsi = gsi_start_phis (info->final_bb); !gsi_end_p (gsi); gsi_next (&gsi))
     {
       gimple phi = gsi_stmt (gsi);
       tree val = PHI_ARG_DEF_FROM_EDGE (phi, e);
       gcc_assert (val);
-      info.default_values[i++] = val;
+      info->default_values[i++] = val;
     }
 }
 
@@ -411,10 +407,10 @@  static void
    order of phi nodes.  SWTCH is the switch statement being converted.  */
 
 static void
-build_constructors (gimple swtch)
+build_constructors (gimple swtch, struct switch_conv_info *info)
 {
   unsigned i, branch_num = gimple_switch_num_labels (swtch);
-  tree pos = info.range_min;
+  tree pos = info->range_min;
 
   for (i = 1; i < branch_num; i++)
     {
@@ -425,8 +421,8 @@  static void
       gimple_stmt_iterator gsi;
       int j;
 
-      if (bb == info.final_bb)
-	e = find_edge (info.switch_bb, bb);
+      if (bb == info->final_bb)
+	e = find_edge (info->switch_bb, bb);
       else
 	e = single_succ_edge (bb);
       gcc_assert (e);
@@ -434,15 +430,15 @@  static void
       while (tree_int_cst_lt (pos, CASE_LOW (cs)))
 	{
 	  int k;
-	  for (k = 0; k < info.phi_count; k++)
+	  for (k = 0; k < info->phi_count; k++)
 	    {
 	      constructor_elt *elt;
 
 	      elt = VEC_quick_push (constructor_elt,
-				    info.constructors[k], NULL);
+				    info->constructors[k], NULL);
 	      elt->index = int_const_binop (MINUS_EXPR, pos,
-					    info.range_min);
-	      elt->value = info.default_values[k];
+					    info->range_min);
+	      elt->value = info->default_values[k];
 	    }
 
 	  pos = int_const_binop (PLUS_EXPR, pos, integer_one_node);
@@ -454,7 +450,7 @@  static void
 	high = CASE_HIGH (cs);
       else
 	high = CASE_LOW (cs);
-      for (gsi = gsi_start_phis (info.final_bb);
+      for (gsi = gsi_start_phis (info->final_bb);
 	   !gsi_end_p (gsi); gsi_next (&gsi))
 	{
 	  gimple phi = gsi_stmt (gsi);
@@ -467,8 +463,8 @@  static void
 	      constructor_elt *elt;
 
 	      elt = VEC_quick_push (constructor_elt,
-				    info.constructors[j], NULL);
-	      elt->index = int_const_binop (MINUS_EXPR, pos, info.range_min);
+				    info->constructors[j], NULL);
+	      elt->index = int_const_binop (MINUS_EXPR, pos, info->range_min);
 	      elt->value = val;
 
 	      pos = int_const_binop (PLUS_EXPR, pos, integer_one_node);
@@ -505,9 +501,10 @@  constructor_contains_same_values_p (VEC (construct
    all the constants.  */
 
 static tree
-array_value_type (gimple swtch, tree type, int num)
+array_value_type (gimple swtch, tree type, int num,
+		  struct switch_conv_info *info)
 {
-  unsigned int i, len = VEC_length (constructor_elt, info.constructors[num]);
+  unsigned int i, len = VEC_length (constructor_elt, info->constructors[num]);
   constructor_elt *elt;
   enum machine_mode mode;
   int sign = 0;
@@ -523,7 +520,7 @@  static tree
   if (len < (optimize_bb_for_size_p (gimple_bb (swtch)) ? 2 : 32))
     return type;
 
-  FOR_EACH_VEC_ELT (constructor_elt, info.constructors[num], i, elt)
+  FOR_EACH_VEC_ELT (constructor_elt, info->constructors[num], i, elt)
     {
       double_int cst;
 
@@ -585,37 +582,37 @@  static tree
 
 static void
 build_one_array (gimple swtch, int num, tree arr_index_type, gimple phi,
-		 tree tidx)
+		 tree tidx, struct switch_conv_info *info)
 {
   tree name, cst;
   gimple load;
   gimple_stmt_iterator gsi = gsi_for_stmt (swtch);
   location_t loc = gimple_location (swtch);
 
-  gcc_assert (info.default_values[num]);
+  gcc_assert (info->default_values[num]);
 
   name = make_ssa_name (SSA_NAME_VAR (PHI_RESULT (phi)), NULL);
-  info.target_inbound_names[num] = name;
+  info->target_inbound_names[num] = name;
 
-  cst = constructor_contains_same_values_p (info.constructors[num]);
+  cst = constructor_contains_same_values_p (info->constructors[num]);
   if (cst)
     load = gimple_build_assign (name, cst);
   else
     {
       tree array_type, ctor, decl, value_type, fetch, default_type;
 
-      default_type = TREE_TYPE (info.default_values[num]);
-      value_type = array_value_type (swtch, default_type, num);
+      default_type = TREE_TYPE (info->default_values[num]);
+      value_type = array_value_type (swtch, default_type, num, info);
       array_type = build_array_type (value_type, arr_index_type);
       if (default_type != value_type)
 	{
 	  unsigned int i;
 	  constructor_elt *elt;
 
-	  FOR_EACH_VEC_ELT (constructor_elt, info.constructors[num], i, elt)
+	  FOR_EACH_VEC_ELT (constructor_elt, info->constructors[num], i, elt)
 	    elt->value = fold_convert (value_type, elt->value);
 	}
-      ctor = build_constructor (array_type, info.constructors[num]);
+      ctor = build_constructor (array_type, info->constructors[num]);
       TREE_CONSTANT (ctor) = true;
       TREE_STATIC (ctor) = true;
 
@@ -645,7 +642,7 @@  build_one_array (gimple swtch, int num, tree arr_i
   SSA_NAME_DEF_STMT (name) = load;
   gsi_insert_before (&gsi, load, GSI_SAME_STMT);
   update_stmt (load);
-  info.arr_ref_last = load;
+  info->arr_ref_last = load;
 }
 
 /* Builds and initializes static arrays initialized with values gathered from
@@ -653,7 +650,7 @@  build_one_array (gimple swtch, int num, tree arr_i
    them.  */
 
 static void
-build_arrays (gimple swtch)
+build_arrays (gimple swtch, struct switch_conv_info *info)
 {
   tree arr_index_type;
   tree tidx, sub, tmp, utype;
@@ -665,19 +662,19 @@  static void
   gsi = gsi_for_stmt (swtch);
 
   /* Make sure we do not generate arithmetics in a subrange.  */
-  utype = TREE_TYPE (info.index_expr);
+  utype = TREE_TYPE (info->index_expr);
   if (TREE_TYPE (utype))
     utype = lang_hooks.types.type_for_mode (TYPE_MODE (TREE_TYPE (utype)), 1);
   else
     utype = lang_hooks.types.type_for_mode (TYPE_MODE (utype), 1);
 
-  arr_index_type = build_index_type (info.range_size);
+  arr_index_type = build_index_type (info->range_size);
   tmp = create_tmp_var (utype, "csui");
   add_referenced_var (tmp);
   tidx = make_ssa_name (tmp, NULL);
   sub = fold_build2_loc (loc, MINUS_EXPR, utype,
-			 fold_convert_loc (loc, utype, info.index_expr),
-			 fold_convert_loc (loc, utype, info.range_min));
+			 fold_convert_loc (loc, utype, info->index_expr),
+			 fold_convert_loc (loc, utype, info->range_min));
   sub = force_gimple_operand_gsi (&gsi, sub,
 				  false, NULL, true, GSI_SAME_STMT);
   stmt = gimple_build_assign (tidx, sub);
@@ -685,29 +682,29 @@  static void
 
   gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
   update_stmt (stmt);
-  info.arr_ref_first = stmt;
+  info->arr_ref_first = stmt;
 
-  for (gsi = gsi_start_phis (info.final_bb), i = 0;
+  for (gsi = gsi_start_phis (info->final_bb), i = 0;
        !gsi_end_p (gsi); gsi_next (&gsi), i++)
-    build_one_array (swtch, i, arr_index_type, gsi_stmt (gsi), tidx);
+    build_one_array (swtch, i, arr_index_type, gsi_stmt (gsi), tidx, info);
 }
 
 /* Generates and appropriately inserts loads of default values at the position
    given by BSI.  Returns the last inserted statement.  */
 
 static gimple
-gen_def_assigns (gimple_stmt_iterator *gsi)
+gen_def_assigns (gimple_stmt_iterator *gsi, struct switch_conv_info *info)
 {
   int i;
   gimple assign = NULL;
 
-  for (i = 0; i < info.phi_count; i++)
+  for (i = 0; i < info->phi_count; i++)
     {
       tree name
-	= make_ssa_name (SSA_NAME_VAR (info.target_inbound_names[i]), NULL);
+	= make_ssa_name (SSA_NAME_VAR (info->target_inbound_names[i]), NULL);
 
-      info.target_outbound_names[i] = name;
-      assign = gimple_build_assign (name, info.default_values[i]);
+      info->target_outbound_names[i] = name;
+      assign = gimple_build_assign (name, info->default_values[i]);
       SSA_NAME_DEF_STMT (name) = assign;
       gsi_insert_before (gsi, assign, GSI_SAME_STMT);
       update_stmt (assign);
@@ -743,7 +740,8 @@  prune_bbs (basic_block bbd, basic_block final)
    bbf description in the comment below).  */
 
 static void
-fix_phi_nodes (edge e1f, edge e2f, basic_block bbf)
+fix_phi_nodes (edge e1f, edge e2f, basic_block bbf,
+	       struct switch_conv_info *info)
 {
   gimple_stmt_iterator gsi;
   int i;
@@ -752,10 +750,9 @@  static void
        !gsi_end_p (gsi); gsi_next (&gsi), i++)
     {
       gimple phi = gsi_stmt (gsi);
-      add_phi_arg (phi, info.target_inbound_names[i], e1f, UNKNOWN_LOCATION);
-      add_phi_arg (phi, info.target_outbound_names[i], e2f, UNKNOWN_LOCATION);
+      add_phi_arg (phi, info->target_inbound_names[i], e1f, UNKNOWN_LOCATION);
+      add_phi_arg (phi, info->target_outbound_names[i], e2f, UNKNOWN_LOCATION);
     }
-
 }
 
 /* Creates a check whether the switch expression value actually falls into the
@@ -780,7 +777,7 @@  static void
 */
 
 static void
-gen_inbound_check (gimple swtch)
+gen_inbound_check (gimple swtch, struct switch_conv_info *info)
 {
   tree label_decl1 = create_artificial_label (UNKNOWN_LOCATION);
   tree label_decl2 = create_artificial_label (UNKNOWN_LOCATION);
@@ -797,17 +794,17 @@  static void
   edge e01, e02, e21, e1d, e1f, e2f;
   location_t loc = gimple_location (swtch);
 
-  gcc_assert (info.default_values);
+  gcc_assert (info->default_values);
   bb0 = gimple_bb (swtch);
 
-  tidx = gimple_assign_lhs (info.arr_ref_first);
+  tidx = gimple_assign_lhs (info->arr_ref_first);
   utype = TREE_TYPE (tidx);
 
   /* (end of) block 0 */
-  gsi = gsi_for_stmt (info.arr_ref_first);
+  gsi = gsi_for_stmt (info->arr_ref_first);
   gsi_next (&gsi);
 
-  bound = fold_convert_loc (loc, utype, info.range_size);
+  bound = fold_convert_loc (loc, utype, info->range_size);
   cond_stmt = gimple_build_cond (LE_EXPR, tidx, bound, NULL_TREE, NULL_TREE);
   gsi_insert_before (&gsi, cond_stmt, GSI_SAME_STMT);
   update_stmt (cond_stmt);
@@ -815,14 +812,14 @@  static void
   /* block 2 */
   label2 = gimple_build_label (label_decl2);
   gsi_insert_before (&gsi, label2, GSI_SAME_STMT);
-  last_assign = gen_def_assigns (&gsi);
+  last_assign = gen_def_assigns (&gsi, info);
 
   /* block 1 */
   label1 = gimple_build_label (label_decl1);
   gsi_insert_before (&gsi, label1, GSI_SAME_STMT);
 
   /* block F */
-  gsi = gsi_start_bb (info.final_bb);
+  gsi = gsi_start_bb (info->final_bb);
   label3 = gimple_build_label (label_decl3);
   gsi_insert_before (&gsi, label3, GSI_SAME_STMT);
 
@@ -834,40 +831,40 @@  static void
   bb1 = e21->dest;
   remove_edge (e21);
 
-  e1d = split_block (bb1, info.arr_ref_last);
+  e1d = split_block (bb1, info->arr_ref_last);
   bbd = e1d->dest;
   remove_edge (e1d);
 
   /* flags and profiles of the edge for in-range values */
   e01 = make_edge (bb0, bb1, EDGE_TRUE_VALUE);
-  e01->probability = REG_BR_PROB_BASE - info.default_prob;
-  e01->count = info.other_count;
+  e01->probability = REG_BR_PROB_BASE - info->default_prob;
+  e01->count = info->other_count;
 
   /* flags and profiles of the edge taking care of out-of-range values */
   e02->flags &= ~EDGE_FALLTHRU;
   e02->flags |= EDGE_FALSE_VALUE;
-  e02->probability = info.default_prob;
-  e02->count = info.default_count;
+  e02->probability = info->default_prob;
+  e02->count = info->default_count;
 
-  bbf = info.final_bb;
+  bbf = info->final_bb;
 
   e1f = make_edge (bb1, bbf, EDGE_FALLTHRU);
   e1f->probability = REG_BR_PROB_BASE;
-  e1f->count = info.other_count;
+  e1f->count = info->other_count;
 
   e2f = make_edge (bb2, bbf, EDGE_FALLTHRU);
   e2f->probability = REG_BR_PROB_BASE;
-  e2f->count = info.default_count;
+  e2f->count = info->default_count;
 
   /* frequencies of the new BBs */
   bb1->frequency = EDGE_FREQUENCY (e01);
   bb2->frequency = EDGE_FREQUENCY (e02);
   bbf->frequency = EDGE_FREQUENCY (e1f) + EDGE_FREQUENCY (e2f);
 
-  prune_bbs (bbd, info.final_bb); /* To keep calc_dfs_tree() in dominance.c
+  prune_bbs (bbd, info->final_bb); /* To keep calc_dfs_tree() in dominance.c
 				     happy.  */
 
-  fix_phi_nodes (e1f, e2f, bbf);
+  fix_phi_nodes (e1f, e2f, bbf, info);
 
   free_dominance_info (CDI_DOMINATORS);
   free_dominance_info (CDI_POST_DOMINATORS);
@@ -875,25 +872,25 @@  static void
 
 /* The following function is invoked on every switch statement (the current one
    is given in SWTCH) and runs the individual phases of switch conversion on it
-   one after another until one fails or the conversion is completed.  */
+   one after another until one fails or the conversion is completed.
+   Returns NULL on success, or a pointer to a string with the reason why the
+   conversion failed.  */
 
-static bool
+static const char *
 process_switch (gimple swtch)
 {
   unsigned int i, branch_num = gimple_switch_num_labels (swtch);
   tree index_type;
+  struct switch_conv_info info;
 
   /* Operand 2 is either NULL_TREE or a vector of cases (stmt.c).  */
   if (branch_num < 2)
-    {
-      info.reason = "switch has no labels\n";
-      return false;
-    }
+    return "switch has no labels";
 
+  info.reason = NULL;
   info.final_bb = NULL;
   info.switch_bb = gimple_bb (swtch);
   info.index_expr = gimple_switch_index (swtch);
-  index_type = TREE_TYPE (info.index_expr);
   info.arr_ref_first = NULL;
   info.arr_ref_last = NULL;
   info.default_prob = 0;
@@ -906,24 +903,26 @@  process_switch (gimple swtch)
 
   /* An ERROR_MARK occurs for various reasons including invalid data type.
      (comment from stmt.c) */
+  index_type = TREE_TYPE (info.index_expr);
   if (index_type == error_mark_node)
+    return "index error\n";
+
+  /* Check the case label values are within reasonable range:  */
+  if (!check_range (swtch, &info))
     {
-      info.reason = "index error.\n";
-      return false;
+      gcc_assert (info.reason);
+      return info.reason;
     }
 
-  /* Check the case label values are within reasonable range:  */
-  if (!check_range (swtch))
-    return false;
-
   /* For all the cases, see whether they are empty, the assignments they
      represent constant and so on...  */
   for (i = 0; i < branch_num; i++)
-    if (!check_process_case (gimple_switch_label (swtch, i)))
+    if (!check_process_case (gimple_switch_label (swtch, i), &info))
       {
+	gcc_assert (info.reason);
 	if (dump_file)
-	  fprintf (dump_file, "Processing of case %i failed\n", i);
-	return false;
+	  fprintf (dump_file, "processing of case %i failed\n\t", i);
+	return info.reason;
       }
 
   if (info.bit_test_uniq <= 2)
@@ -933,27 +932,29 @@  process_switch (gimple swtch)
 					   info.range_size, info.bit_test_uniq,
 					   info.bit_test_count))
 	{
-	  info.reason = "  Expanding as bit test is preferable\n";
-	  return false;
+	  return "expanding as bit test is preferable";
 	}
     }
 
-  if (!check_final_bb ())
-    return false;
+  if (!check_final_bb (&info))
+    {
+      gcc_assert (info.reason);
+      return info.reason;
+    }
 
   /* At this point all checks have passed and we can proceed with the
      transformation.  */
 
-  create_temp_arrays ();
-  gather_default_values (gimple_switch_label (swtch, 0));
-  build_constructors (swtch);
+  create_temp_arrays (&info);
+  gather_default_values (gimple_switch_label (swtch, 0), &info);
+  build_constructors (swtch, &info);
 
-  build_arrays (swtch); /* Build the static arrays and assignments.   */
-  gen_inbound_check (swtch);	/* Build the bounds check.  */
+  build_arrays (swtch, &info); /* Build the static arrays and assignments.   */
+  gen_inbound_check (swtch, &info);	/* Build the bounds check.  */
 
   /* Cleanup:  */
-  free_temp_arrays ();
-  return true;
+  free_temp_arrays (&info);
+  return NULL;
 }
 
 /* The main function of the pass scans statements for switches and invokes
@@ -966,6 +967,7 @@  do_switchconv (void)
 
   FOR_EACH_BB (bb)
   {
+    const char *failure_reason;
     gimple stmt = last_stmt (bb);
     if (stmt && gimple_code (stmt) == GIMPLE_SWITCH)
       {
@@ -980,8 +982,8 @@  do_switchconv (void)
 	    putc ('\n', dump_file);
 	  }
 
-	info.reason = NULL;
-	if (process_switch (stmt))
+	failure_reason = process_switch (stmt);
+	if (! failure_reason)
 	  {
 	    if (dump_file)
 	      {
@@ -993,10 +995,9 @@  do_switchconv (void)
 	  {
 	    if (dump_file)
 	      {
-		gcc_assert (info.reason);
 		fputs ("Bailing out - ", dump_file);
-		fputs (info.reason, dump_file);
-		fputs ("--------------------------------\n", dump_file);
+		fputs (failure_reason, dump_file);
+		fputs ("\n--------------------------------\n", dump_file);
 	      }
 	  }
       }
Index: stmt.c
===================================================================
--- stmt.c	(revision 186526)
+++ stmt.c	(working copy)
@@ -1822,66 +1822,25 @@  expand_stack_restore (tree var)
    fed to us in descending order from the sorted vector of case labels used
    in the tree part of the middle end.  So the list we construct is
    sorted in ascending order.  The bounds on the case range, LOW and HIGH,
-   are converted to case's index type TYPE.  */
+   are converted to case's index type TYPE.  Note that the original type
+   of the case index in the source code is usually "lost" during
+   gimplification due to type promotion, but the case labels retain the
+   original type.  */
 
 static struct case_node *
 add_case_node (struct case_node *head, tree type, tree low, tree high,
                tree label, alloc_pool case_node_pool)
 {
-  tree min_value, max_value;
   struct case_node *r;
 
-  gcc_assert (TREE_CODE (low) == INTEGER_CST);
-  gcc_assert (!high || TREE_CODE (high) == INTEGER_CST);
+  gcc_checking_assert (low);
+  gcc_checking_assert (! high || (TREE_TYPE (low) == TREE_TYPE (high)));
 
-  min_value = TYPE_MIN_VALUE (type);
-  max_value = TYPE_MAX_VALUE (type);
-
-  /* If there's no HIGH value, then this is not a case range; it's
-     just a simple case label.  But that's just a degenerate case
-     range.
-     If the bounds are equal, turn this into the one-value case.  */
-  if (!high || tree_int_cst_equal (low, high))
-    {
-      /* If the simple case value is unreachable, ignore it.  */
-      if ((TREE_CODE (min_value) == INTEGER_CST
-            && tree_int_cst_compare (low, min_value) < 0)
-	  || (TREE_CODE (max_value) == INTEGER_CST
-	      && tree_int_cst_compare (low, max_value) > 0))
-	return head;
-      low = fold_convert (type, low);
-      high = low;
-    }
-  else
-    {
-      /* If the entire case range is unreachable, ignore it.  */
-      if ((TREE_CODE (min_value) == INTEGER_CST
-            && tree_int_cst_compare (high, min_value) < 0)
-	  || (TREE_CODE (max_value) == INTEGER_CST
-	      && tree_int_cst_compare (low, max_value) > 0))
-	return head;
-
-      /* If the lower bound is less than the index type's minimum
-	 value, truncate the range bounds.  */
-      if (TREE_CODE (min_value) == INTEGER_CST
-            && tree_int_cst_compare (low, min_value) < 0)
-	low = min_value;
-      low = fold_convert (type, low);
-
-      /* If the upper bound is greater than the index type's maximum
-	 value, truncate the range bounds.  */
-      if (TREE_CODE (max_value) == INTEGER_CST
-	  && tree_int_cst_compare (high, max_value) > 0)
-	high = max_value;
-      high = fold_convert (type, high);
-    }
-
-
   /* Add this label to the chain.  Make sure to drop overflow flags.  */
   r = (struct case_node *) pool_alloc (case_node_pool);
-  r->low = build_int_cst_wide (TREE_TYPE (low), TREE_INT_CST_LOW (low),
+  r->low = build_int_cst_wide (type, TREE_INT_CST_LOW (low),
 			       TREE_INT_CST_HIGH (low));
-  r->high = build_int_cst_wide (TREE_TYPE (high), TREE_INT_CST_LOW (high),
+  r->high = build_int_cst_wide (type, TREE_INT_CST_LOW (high),
 				TREE_INT_CST_HIGH (high));
   r->code_label = label;
   r->parent = r->left = NULL;
@@ -2151,9 +2110,12 @@  expand_case (gimple stmt)
 	  gcc_assert (low);
 	  high = CASE_HIGH (elt);
 
-	  /* Discard empty ranges.  */
-	  if (high && tree_int_cst_lt (high, low))
-	    continue;
+	  /* The canonical from of a case label in GIMPLE is that a simple case
+	     has an empty CASE_HIGH.  For the casesi and tablejump expanders,
+	     the back ends want simple cases to have high == low.  */
+	  gcc_assert (! high || tree_int_cst_lt (low, high));
+	  if (! high)
+	    high = low;
 
 	  case_list = add_case_node (case_list, index_type, low, high,
                                      CASE_LABEL (elt), case_node_pool);
@@ -2199,16 +2161,10 @@  expand_case (gimple stmt)
       BITMAP_FREE (label_bitmap);
 
       /* cleanup_tree_cfg removes all SWITCH_EXPR with a single
-	 destination, such as one with a default case only.  However,
-	 it doesn't remove cases that are out of range for the switch
-	 type, so we may still get a zero here.  */
-      if (count == 0)
-	{
-	  if (default_label)
-	    emit_jump (default_label);
-          free_alloc_pool (case_node_pool);
-	  return;
-	}
+	 destination, such as one with a default case only.
+	 It also removes cases that are out of range for the switch
+	 type, so we should never get a zero here.  */
+      gcc_assert (count > 0);
 
       /* Compute span of values.  */
       range = fold_build2 (MINUS_EXPR, index_type, maxval, minval);
Index: gimplify.c
===================================================================
--- gimplify.c	(revision 186526)
+++ gimplify.c	(working copy)
@@ -1575,6 +1575,9 @@  gimplify_switch_expr (tree *expr_p, gimple_seq *pr
   tree switch_expr = *expr_p;
   gimple_seq switch_body_seq = NULL;
   enum gimplify_status ret;
+  tree index_type = TREE_TYPE (switch_expr);
+  if (index_type == void_type_node)
+    index_type = TREE_TYPE (SWITCH_COND (switch_expr));
 
   ret = gimplify_expr (&SWITCH_COND (switch_expr), pre_p, NULL, is_gimple_val,
                        fb_rvalue);
@@ -1585,6 +1588,7 @@  gimplify_switch_expr (tree *expr_p, gimple_seq *pr
     {
       VEC (tree,heap) *labels;
       VEC (tree,heap) *saved_labels;
+      tree min_value, max_value;
       tree default_case = NULL_TREE;
       size_t i, len;
       gimple gimple_switch;
@@ -1593,7 +1597,7 @@  gimplify_switch_expr (tree *expr_p, gimple_seq *pr
 	 be bothered to null out the body too.  */
       gcc_assert (!SWITCH_LABELS (switch_expr));
 
-      /* save old labels, get new ones from body, then restore the old
+      /* Save old labels, get new ones from body, then restore the old
          labels.  Save all the things from the switch body to append after.  */
       saved_labels = gimplify_ctxp->case_labels;
       gimplify_ctxp->case_labels = VEC_alloc (tree, heap, 8);
@@ -1603,18 +1607,82 @@  gimplify_switch_expr (tree *expr_p, gimple_seq *pr
       gimplify_ctxp->case_labels = saved_labels;
 
       i = 0;
+      min_value = TYPE_MIN_VALUE (index_type);
+      max_value = TYPE_MAX_VALUE (index_type);
       while (i < VEC_length (tree, labels))
 	{
 	  tree elt = VEC_index (tree, labels, i);
 	  tree low = CASE_LOW (elt);
+	  tree high = CASE_HIGH (elt);
 	  bool remove_element = FALSE;
 
+
 	  if (low)
 	    {
-	      /* Discard empty ranges.  */
-	      tree high = CASE_HIGH (elt);
-	      if (high && tree_int_cst_lt (high, low))
-	        remove_element = TRUE;
+	      gcc_checking_assert (TREE_CODE (low) == INTEGER_CST);
+	      gcc_checking_assert (!high || TREE_CODE (high) == INTEGER_CST);
+
+	      /* This is a non-default case label, i.e. it has a value.
+
+		 See if the case label is reachable within the range of
+		 the index type.  Remove out-of-range case values.  Turn
+		 case ranges into a canonical form (high > low strictly)
+		 and convert the case label values to the index type.
+
+		 NB: The type of gimple_switch_index() may be the promoted
+		 type, but the case labels retain the original type.  */
+
+	      if (high)
+		{
+		  /* This is a case range.  Discard empty ranges.
+		     If the bounds or the range are equal, turn this
+		     into a simple (one-value) case.  */
+		  int cmp = tree_int_cst_compare (high, low);
+		  if (cmp < 0)
+		    remove_element = TRUE;
+		  else if (cmp == 0)
+		    high = NULL_TREE;
+		}
+
+	      if (! high)
+		{
+		  /* If the simple case value is unreachable, ignore it.  */
+		  if ((TREE_CODE (min_value) == INTEGER_CST
+		       && tree_int_cst_compare (low, min_value) < 0)
+		      || (TREE_CODE (max_value) == INTEGER_CST
+			  && tree_int_cst_compare (low, max_value) > 0))
+		    remove_element = TRUE;
+		  else
+		    low = fold_convert (index_type, low);
+		}
+	      else
+		{
+		  /* If the entire case range is unreachable, ignore it.  */
+		  if ((TREE_CODE (min_value) == INTEGER_CST
+		       && tree_int_cst_compare (high, min_value) < 0)
+		      || (TREE_CODE (max_value) == INTEGER_CST
+			  && tree_int_cst_compare (low, max_value) > 0))
+		    remove_element = TRUE;
+		  else
+		    {
+		      /* If the lower bound is less than the index type's
+			 minimum value, truncate the range bounds.  */
+		      if (TREE_CODE (min_value) == INTEGER_CST
+			  && tree_int_cst_compare (low, min_value) < 0)
+			low = min_value;
+		      low = fold_convert (index_type, low);
+
+		      /* If the upper bound is greater than the index type's
+			 maximum value, truncate the range bounds.  */
+		      if (TREE_CODE (max_value) == INTEGER_CST
+			  && tree_int_cst_compare (high, max_value) > 0)
+			high = max_value;
+		      high = fold_convert (index_type, high);
+		    }
+		}
+
+	      CASE_LOW (elt) = low;
+	      CASE_HIGH (elt) = high;
 	    }
 	  else
 	    {
@@ -1636,25 +1704,21 @@  gimplify_switch_expr (tree *expr_p, gimple_seq *pr
 
       if (!default_case)
 	{
-	  tree type = TREE_TYPE (switch_expr);
-
 	  /* If the switch has no default label, add one, so that we jump
 	     around the switch body.  If the labels already cover the whole
-	     range of type, add the default label pointing to one of the
-	     existing labels.  */
-	  if (type == void_type_node)
-	    type = TREE_TYPE (SWITCH_COND (switch_expr));
+	     range of the switch index_type, add the default label pointing
+	     to one of the existing labels.  */
 	  if (len
-	      && INTEGRAL_TYPE_P (type)
-	      && TYPE_MIN_VALUE (type)
-	      && TYPE_MAX_VALUE (type)
+	      && INTEGRAL_TYPE_P (index_type)
+	      && TYPE_MIN_VALUE (index_type)
+	      && TYPE_MAX_VALUE (index_type)
 	      && tree_int_cst_equal (CASE_LOW (VEC_index (tree, labels, 0)),
-				     TYPE_MIN_VALUE (type)))
+				     TYPE_MIN_VALUE (index_type)))
 	    {
 	      tree low, high = CASE_HIGH (VEC_index (tree, labels, len - 1));
 	      if (!high)
 		high = CASE_LOW (VEC_index (tree, labels, len - 1));
-	      if (tree_int_cst_equal (high, TYPE_MAX_VALUE (type)))
+	      if (tree_int_cst_equal (high, TYPE_MAX_VALUE (index_type)))
 		{
 		  for (i = 1; i < len; i++)
 		    {