2010-12-13  Tom de Vries  <tom@codesourcery.com>

	gcc/
	* tree-if-switch-conversion.c: New pass.
	* tree-pass.h (pass_if_to_switch): Declare.
	* common.opt (ftree-if-to-switch-conversion): New switch.
	* opts.c (decode_options): Set flag_tree_if_to_switch_conversion at -O2
	and higher.
	* passes.c (init_optimization_passes): Use new pass.
	* params.def (PARAM_IF_TO_SWITCH_THRESHOLD): New param.
	* doc/invoke.texi (-ftree-if-to-switch-conversion)
	(if-to-switch-threshold): New item.
	* doc/invoke.texi (Optimization Options, option -O2): Add
	-ftree-if-to-switch-conversion.
	* Makefile.in (OBJS-common): Add tree-if-switch-conversion.o.
	* Makefile.in (tree-if-switch-conversion.o): New rule.

=== modified file 'gcc/Makefile.in'
Index: gcc-4_5-branch/gcc/Makefile.in
===================================================================
--- gcc-4_5-branch.orig/gcc/Makefile.in
+++ gcc-4_5-branch/gcc/Makefile.in
@@ -1354,6 +1354,7 @@ OBJS-common = \
 	tree-profile.o \
 	tree-scalar-evolution.o \
 	tree-sra.o \
+	tree-if-switch-conversion.o \
 	tree-switch-conversion.o \
 	tree-ssa-address.o \
 	tree-ssa-alias.o \
@@ -3013,6 +3014,11 @@ tree-sra.o : tree-sra.c $(CONFIG_H) $(SY
    $(TM_H) $(TREE_H) $(GIMPLE_H) $(CGRAPH_H) $(TREE_FLOW_H) $(IPA_PROP_H) \
    $(DIAGNOSTIC_H) statistics.h $(TREE_DUMP_H) $(TIMEVAR_H) $(PARAMS_H) \
    $(TARGET_H) $(FLAGS_H) $(EXPR_H) $(TREE_INLINE_H)
+tree-if-switch-conversion.o : tree-if-switch-conversion.c $(CONFIG_H) \
+    $(SYSTEM_H) $(TREE_H) $(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_H) \
+    $(TREE_INLINE_H) $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \
+    $(GIMPLE_H) $(TREE_PASS_H) $(FLAGS_H) $(EXPR_H) $(BASIC_BLOCK_H) output.h \
+    $(GGC_H) $(OBSTACK_H) $(PARAMS_H) $(CPPLIB_H) $(PARAMS_H)
 tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \
     $(TREE_H) $(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_H) $(TREE_INLINE_H) \
     $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) $(GIMPLE_H) \
Index: gcc-4_5-branch/gcc/common.opt
===================================================================
--- gcc-4_5-branch.orig/gcc/common.opt
+++ gcc-4_5-branch/gcc/common.opt
@@ -1285,6 +1285,10 @@ ftree-switch-conversion
 Common Report Var(flag_tree_switch_conversion) Optimization
 Perform conversions of switch initializations.
 
+ftree-if-to-switch-conversion
+Common Report Var(flag_tree_if_to_switch_conversion) Optimization
+Perform conversions of chains of ifs into switches.
+
 ftree-dce
 Common Report Var(flag_tree_dce) Optimization
 Enable SSA dead code elimination optimization on trees
Index: gcc-4_5-branch/gcc/doc/invoke.texi
===================================================================
--- gcc-4_5-branch.orig/gcc/doc/invoke.texi
+++ gcc-4_5-branch/gcc/doc/invoke.texi
@@ -382,7 +382,8 @@ Objective-C and Objective-C++ Dialects}.
 -fstrict-aliasing -fstrict-overflow -fthread-jumps -ftracer @gol
 -ftree-builtin-call-dce -ftree-ccp -ftree-ch -ftree-copy-prop @gol
 -ftree-copyrename -ftree-dce @gol
--ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre -ftree-loop-im @gol
+-ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre @gol
+-ftree-if-to-switch-conversion -ftree-loop-im @gol
 -ftree-phiprop -ftree-loop-distribution @gol
 -ftree-loop-ivcanon -ftree-loop-linear -ftree-loop-optimize @gol
 -ftree-parallelize-loops=@var{n} -ftree-pre -ftree-pta -ftree-reassoc @gol
@@ -5798,6 +5799,7 @@ also turns on the following optimization
 -fsched-interblock  -fsched-spec @gol
 -fschedule-insns  -fschedule-insns2 @gol
 -fstrict-aliasing -fstrict-overflow @gol
+-ftree-if-to-switch-conversion @gol
 -ftree-switch-conversion @gol
 -ftree-pre @gol
 -ftree-vrp}
@@ -6634,6 +6636,10 @@ Perform conversion of simple initializat
 initializations from a scalar array.  This flag is enabled by default
 at @option{-O2} and higher.
 
+@item -ftree-if-to-switch-conversion
+Perform conversion of chains of ifs into switches.  This flag is enabled by
+default at @option{-O2} and higher.
+
 @item -ftree-dce
 @opindex ftree-dce
 Perform dead code elimination (DCE) on trees.  This flag is enabled by
@@ -8577,6 +8583,12 @@ loop in the loop nest by a given number 
 length can be changed using the @option{loop-block-tile-size}
 parameter.  The default value is 51 iterations.
 
+@item if-to-switch-threshold
+If-chain to switch conversion, enabled by
+@option{-ftree-if-to-switch-conversion} convert chains of ifs of sufficient
+length into switches.  The parameter @option{if-to-switch-threshold} can be
+used to set the minimal required length.  The default value is 3.
+
 @end table
 @end table
 
Index: gcc-4_5-branch/gcc/opts.c
===================================================================
--- gcc-4_5-branch.orig/gcc/opts.c
+++ gcc-4_5-branch/gcc/opts.c
@@ -905,6 +905,7 @@ decode_options (unsigned int argc, const
   flag_tree_builtin_call_dce = opt2;
   flag_tree_pre = opt2;
   flag_tree_switch_conversion = opt2;
+  flag_tree_if_to_switch_conversion = opt2;
   flag_ipa_cp = opt2;
   flag_ipa_sra = opt2;
   flag_ee = opt2;
Index: gcc-4_5-branch/gcc/params.def
===================================================================
--- gcc-4_5-branch.orig/gcc/params.def
+++ gcc-4_5-branch/gcc/params.def
@@ -826,6 +826,11 @@ DEFPARAM (PARAM_IPA_SRA_PTR_GROWTH_FACTO
 	  "a pointer to an aggregate with",
 	  2, 0, 0)
 
+DEFPARAM (PARAM_IF_TO_SWITCH_THRESHOLD,
+	  "if-to-switch-threshold",
+	  "Threshold for converting an if-chain into a switch",
+	  3, 0, 0)
+
 /*
 Local variables:
 mode:c
Index: gcc-4_5-branch/gcc/passes.c
===================================================================
--- gcc-4_5-branch.orig/gcc/passes.c
+++ gcc-4_5-branch/gcc/passes.c
@@ -788,6 +788,7 @@ init_optimization_passes (void)
 	  NEXT_PASS (pass_cd_dce);
 	  NEXT_PASS (pass_early_ipa_sra);
 	  NEXT_PASS (pass_tail_recursion);
+	  NEXT_PASS (pass_if_to_switch);
 	  NEXT_PASS (pass_convert_switch);
           NEXT_PASS (pass_cleanup_eh);
           NEXT_PASS (pass_profile);
@@ -844,6 +845,7 @@ init_optimization_passes (void)
       NEXT_PASS (pass_phiprop);
       NEXT_PASS (pass_fre);
       NEXT_PASS (pass_copy_prop);
+      NEXT_PASS (pass_if_to_switch);
       NEXT_PASS (pass_merge_phi);
       NEXT_PASS (pass_vrp);
       NEXT_PASS (pass_dce);
Index: gcc-4_5-branch/gcc/tree-if-switch-conversion.c
===================================================================
--- /dev/null
+++ gcc-4_5-branch/gcc/tree-if-switch-conversion.c
@@ -0,0 +1,643 @@
+/* Convert a chain of ifs into a switch.
+   Copyright (C) 2010 Free Software Foundation, Inc.
+   Contributed by Tom de Vries <tom@codesourcery.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
+
+
+/* The following pass converts a chain of ifs into a switch.
+
+   The if-chain has the following properties:
+   - all bbs end in a GIMPLE_COND.
+   - all but the first bb are empty, apart from the GIMPLE_COND.
+   - the GIMPLE_CONDs compare the same variable against integer constants.
+   - the true gotos all target the same bb.
+   - the false gotos target the next in the if-chain.
+
+   F.i., consider the following if-chain:
+   ...
+   <bb 4>:
+   ...
+   if (D.1993_3 == 32)
+     goto <bb 3>;
+   else
+     goto <bb 5>;
+
+   <bb 5>:
+   if (D.1993_3 == 13)
+     goto <bb 3>;
+   else
+     goto <bb 6>;
+
+   <bb 6>:
+   if (D.1993_3 == 10)
+     goto <bb 3>;
+   else
+     goto <bb 7>;
+
+   <bb 7>:
+   if (D.1993_3 == 9)
+     goto <bb 3>;
+   else
+     goto <bb 8>;
+   ...
+
+   The pass will report this if-chain like this:
+   ...
+   var: D.1993_3
+   first: <bb 4>
+   true: <bb 3>
+   last: <bb 7>
+   constants: 9 10 13 32
+   ...
+
+   and then convert the if-chain into a switch:
+   ...
+   <bb 4>:
+   ...
+   switch (D.1993_3) <default: <L8>,
+                      case 9: <L7>,
+                      case 10: <L7>,
+                      case 13: <L7>,
+                      case 32: <L7>>
+   ...
+
+   The conversion does not happen if the chain is too short.  The threshold is
+   determined by the parameter PARAM_IF_TO_SWITCH_THRESHOLD.
+
+   The pass will try to construct a chain for each bb, unless the bb it is
+   already contained in a chain.  This ensures that all chains will be found,
+   and that no chain will be constructed twice.  The pass constructs and
+   converts the chains one-by-one, rather than first calculating all the chains
+   and then doing the conversions.
+
+   The pass could detect range-checks in analyze_bb as well, and handle them.
+   Simple ones, like 'c <= 5', and more complex ones, like
+   '(unsigned char) c + 247 <= 1', which is generated by the C front-end from
+   code like '(c == 9 || c == 10)' or '(9 <= c && c <= 10)'.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+
+#include "params.h"
+#include "flags.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-flow.h"
+#include "tree-flow-inline.h"
+#include "tree-ssa-operands.h"
+#include "diagnostic.h"
+#include "tree-pass.h"
+#include "tree-dump.h"
+#include "timevar.h"
+
+/* Information we've collected about a single bb.  */
+
+struct ifsc_info
+{
+  /* The variable of the bb's ending GIMPLE_COND, NULL_TREE if not present.  */
+  tree var;
+  /* The cond_code of the bb's ending GIMPLE_COND.  */
+  enum tree_code cond_code;
+  /* The constant of the bb's ending GIMPLE_COND.  */
+  tree constant;
+  /* Successor edge of the bb if its GIMPLE_COND is true.  */
+  edge true_edge;
+  /* Successor edge of the bb if its GIMPLE_COND is false.  */
+  edge false_edge;
+  /* Set if the bb has valid ifsc_info.  */
+   bool valid;
+  /* Set if the bb is part of a chain.  */
+  bool chained;
+};
+
+/* Macros to access the fields of struct ifsc_info.  */
+
+#define BB_IFSC_VAR(bb) (((struct ifsc_info *)bb->aux)->var)
+#define BB_IFSC_COND_CODE(bb) (((struct ifsc_info *)bb->aux)->cond_code)
+#define BB_IFSC_CONSTANT(bb) (((struct ifsc_info *)bb->aux)->constant)
+#define BB_IFSC_TRUE_EDGE(bb) (((struct ifsc_info *)bb->aux)->true_edge)
+#define BB_IFSC_FALSE_EDGE(bb) (((struct ifsc_info *)bb->aux)->false_edge)
+#define BB_IFSC_VALID(bb) (((struct ifsc_info *)bb->aux)->valid)
+#define BB_IFSC_CHAINED(bb) (((struct ifsc_info *)bb->aux)->chained)
+
+/* Data-type describing an if-chain.  */
+
+struct if_chain
+{
+  /* First bb in the chain.  */
+  basic_block first;
+  /* Last bb in the chain.  */
+  basic_block last;
+  /* Variable that GIMPLE_CONDs of all bbs in chain compare against.  */
+  tree var;
+  /* bb that all GIMPLE_CONDs jump to if comparison succeeds.  */
+  basic_block true_dest;
+  /* Constants that GIMPLE_CONDs of all bbs in chain compare var against.  */
+  VEC (tree, heap) *constants;
+  /* Same as previous, but sorted and with duplicates removed.  */
+  VEC (tree, heap) *unique_constants;
+};
+
+/* Utility macro.  */
+
+#define SWAP(T, X, Y) do { T tmp = (X); (X) = (Y); (Y) = tmp; } while (0)
+
+/* Helper function for sort_constants.  */
+
+static int
+compare_constants (const void *p1, const void *p2)
+{
+  const_tree const c1 = *(const_tree const*)p1;
+  const_tree const c2 = *(const_tree const*)p2;
+
+  return tree_int_cst_compare (c1, c2);
+}
+
+/* Sort constants in constants and copy to unique_constants, while skipping
+   duplicates.  */
+
+static void
+sort_constants (VEC (tree,heap) *constants, VEC (tree,heap) **unique_constants)
+{
+  size_t len = VEC_length (tree, constants);
+  unsigned int ix;
+  tree prev = NULL_TREE, constant;
+
+  /* Sort constants.  */
+  qsort (VEC_address (tree, constants), len, sizeof (tree),
+         compare_constants);
+
+  /* Copy to unique_constants, while skipping duplicates.  */
+  for (ix = 0; VEC_iterate (tree, constants, ix, constant); ix++)
+    {
+      if (prev != NULL_TREE && tree_int_cst_compare (prev, constant) == 0)
+        continue;
+      prev = constant;
+
+      VEC_safe_push (tree, heap, *unique_constants, constant);
+    }
+}
+
+/* Get true_edge and false_edge of a bb ending in a conditional jump.  */
+
+static void
+get_edges (basic_block bb, edge *true_edge, edge *false_edge)
+{
+  edge e0, e1;
+  int e0_true;
+  int n = EDGE_COUNT (bb->succs);
+  gcc_assert (n == 2);
+
+  e0 = EDGE_SUCC (bb, 0);
+  e1 = EDGE_SUCC (bb, 1);
+
+  e0_true = e0->flags & EDGE_TRUE_VALUE;
+
+  *true_edge = e0_true ? e0 : e1;
+  *false_edge = e0_true ? e1 : e0;
+
+  gcc_assert ((*true_edge)->flags & EDGE_TRUE_VALUE);
+  gcc_assert ((*false_edge)->flags & EDGE_FALSE_VALUE);
+
+  gcc_assert (((*true_edge)->flags & EDGE_FALLTHRU) == 0);
+  gcc_assert (((*false_edge)->flags & EDGE_FALLTHRU) == 0);
+}
+
+/* Analyze bb and store results in ifsc_info struct.  */
+
+static void
+analyze_bb (basic_block bb)
+{
+  gimple stmt = last_stmt (bb);
+  tree lhs, rhs, var, constant;
+  edge true_edge, false_edge;
+  enum tree_code cond_code;
+
+  /* Don't redo analysis.  */
+  if (BB_IFSC_VALID (bb))
+    return;
+  BB_IFSC_VALID (bb) = true;
+
+
+  /* bb needs to end in GIMPLE_COND.  */
+  if (!stmt || gimple_code (stmt) != GIMPLE_COND)
+    return;
+
+  /* bb needs to end in EQ_EXPR or NE_EXPR.  */
+  cond_code = gimple_cond_code (stmt);
+  if (cond_code != EQ_EXPR && cond_code != NE_EXPR)
+    return;
+
+  lhs = gimple_cond_lhs (stmt);
+  rhs = gimple_cond_rhs (stmt);
+
+  /* GIMPLE_COND needs to compare variable to constant.  */
+  if ((TREE_CONSTANT (lhs) == 0)
+      == (TREE_CONSTANT (rhs) == 0))
+    return;
+
+  var = TREE_CONSTANT (lhs) ? rhs : lhs;
+  constant = TREE_CONSTANT (lhs)? lhs : rhs;
+
+  /* Switches cannot handle non-integral types.  */
+  if (!INTEGRAL_TYPE_P(TREE_TYPE (var)))
+    return;
+
+  get_edges (bb, &true_edge, &false_edge);
+
+  if (cond_code == NE_EXPR)
+    SWAP (edge, true_edge, false_edge);
+
+  /* TODO: loosen this constraint.  In principle it's ok if true_edge->dest has
+     phis, as long as for each phi all the edges coming from the chain have the
+     same value.  */
+  if (!gimple_seq_empty_p (phi_nodes (true_edge->dest)))
+    return;
+
+  /* Store analysis in ifsc_info struct.  */
+  BB_IFSC_VAR (bb) = var;
+  BB_IFSC_COND_CODE (bb) = cond_code;
+  BB_IFSC_CONSTANT (bb) = constant;
+  BB_IFSC_TRUE_EDGE (bb) = true_edge;
+  BB_IFSC_FALSE_EDGE (bb) = false_edge;
+}
+
+/* Grow if-chain forward.  */
+
+static void
+grow_if_chain_forward (struct if_chain *chain)
+{
+  basic_block next_bb;
+
+  while (1)
+    {
+      next_bb = BB_IFSC_FALSE_EDGE (chain->last)->dest;
+
+      /* next_bb is already part of another chain.  */
+      if (BB_IFSC_CHAINED (next_bb))
+        break;
+
+      /* next_bb needs to be dominated by the last bb.  */
+      if (!single_pred_p (next_bb))
+        break;
+
+      analyze_bb (next_bb);
+
+      /* Does next_bb fit in chain?  */
+      if (BB_IFSC_VAR (next_bb) != chain->var
+          || BB_IFSC_TRUE_EDGE (next_bb)->dest != chain->true_dest)
+        break;
+
+      /* We can only add empty bbs at the end of the chain.  */
+      if (first_stmt (next_bb) != last_stmt (next_bb))
+        break;
+
+      /* Add next_bb at end of chain.  */
+      VEC_safe_push (tree, heap, chain->constants, BB_IFSC_CONSTANT (next_bb));
+      BB_IFSC_CHAINED (next_bb) = true;
+      chain->last = next_bb;
+    }
+}
+
+/* Grow if-chain backward.  */
+
+static void
+grow_if_chain_backward (struct if_chain *chain)
+{
+  basic_block prev_bb;
+
+  while (1)
+    {
+      /* First bb is not empty, cannot grow backwards.  */
+      if (first_stmt (chain->first) != last_stmt (chain->first))
+        break;
+
+      /* First bb has no single predecessor, cannot grow backwards.  */
+      if (!single_pred_p (chain->first))
+        break;
+
+      prev_bb = single_pred (chain->first);
+
+      /* prev_bb is already part of another chain.  */
+      if (BB_IFSC_CHAINED (prev_bb))
+        break;
+
+      analyze_bb (prev_bb);
+
+      /* Does prev_bb fit in chain?  */
+      if (BB_IFSC_VAR (prev_bb) != chain->var
+          || BB_IFSC_TRUE_EDGE (prev_bb)->dest != chain->true_dest)
+        break;
+
+      /* Add prev_bb at beginning of chain.  */
+      VEC_safe_push (tree, heap, chain->constants, BB_IFSC_CONSTANT (prev_bb));
+      BB_IFSC_CHAINED (prev_bb) = true;
+      chain->first = prev_bb;
+    }
+}
+
+/* Grow if-chain containing bb.  */
+
+static void
+grow_if_chain (basic_block bb, struct if_chain *chain)
+{
+  /* Initialize chain to empty.  */
+  VEC_truncate (tree, chain->constants, 0);
+  VEC_truncate (tree, chain->unique_constants, 0);
+
+  /* bb is already part of another chain.  */
+  if (BB_IFSC_CHAINED (bb))
+    return;
+
+  analyze_bb (bb);
+
+  /* bb is not fit to be part of a chain.  */
+  if (BB_IFSC_VAR (bb) == NULL_TREE)
+    return;
+
+  /* Set bb as initial part of the chain.  */
+  VEC_safe_push (tree, heap, chain->constants, BB_IFSC_CONSTANT (bb));
+  chain->first = chain->last = bb;
+  chain->var = BB_IFSC_VAR (bb);
+  chain->true_dest = BB_IFSC_TRUE_EDGE (bb)->dest;
+
+  /* bb is part of a chain now.  */
+  BB_IFSC_CHAINED (bb) = true;
+
+  /* Grow chain to its maximum size.  */
+  grow_if_chain_forward (chain);
+  grow_if_chain_backward (chain);
+
+  /* Sort constants and skip duplicates.  */
+  sort_constants (chain->constants, &chain->unique_constants);
+}
+
+static void
+dump_tree_vector (VEC (tree, heap) *vec)
+{
+  unsigned int ix;
+  tree constant;
+
+  for (ix = 0; VEC_iterate (tree, vec, ix, constant); ix++)
+    {
+      if (ix != 0)
+        fprintf (dump_file, " ");
+      print_generic_expr (dump_file, constant, 0);
+    }
+  fprintf (dump_file, "\n");
+}
+
+/* Dump if-chain to dump_file.  */
+
+static void
+dump_if_chain (struct if_chain *chain)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file, "var: ");
+  print_generic_expr (dump_file, chain->var, 0);
+  fprintf (dump_file, "\n");
+  fprintf (dump_file, "first: <bb %d>\n", chain->first->index);
+  fprintf (dump_file, "true: <bb %d>\n", chain->true_dest->index);
+  fprintf (dump_file, "last: <bb %d>\n",chain->last->index);
+
+  fprintf (dump_file, "constants: ");
+  dump_tree_vector (chain->constants);
+
+  if (VEC_length (tree, chain->unique_constants)
+      != VEC_length (tree, chain->constants))
+    {
+      fprintf (dump_file, "unique_constants: ");
+      dump_tree_vector (chain->unique_constants);
+    }
+}
+
+/* Remove redundant bbs and edges.  */
+
+static void
+remove_redundant_bbs_and_edges (struct if_chain *chain, int *false_prob)
+{
+  basic_block bb, next;
+  edge true_edge, false_edge;
+
+  for (bb = chain->first;; bb = next)
+    {
+      true_edge = BB_IFSC_TRUE_EDGE (bb);
+      false_edge = BB_IFSC_FALSE_EDGE (bb);
+
+      /* Determine next, before we delete false_edge.  */
+      next = false_edge->dest;
+
+      /* Accumulate probability.  */
+      *false_prob = (*false_prob * false_edge->probability) / REG_BR_PROB_BASE;
+
+      /* Don't remove the new true_edge.  */
+      if (bb != chain->first)
+        remove_edge (true_edge);
+
+      /* Don't remove the new false_edge.  */
+      if (bb != chain->last)
+        remove_edge (false_edge);
+
+      /* Don't remove the first bb.  */
+      if (bb != chain->first)
+        delete_basic_block (bb);
+
+      /* Stop after last.  */
+      if (bb == chain->last)
+        break;
+    }
+}
+
+/* Update control flow graph.  */
+
+static void
+update_cfg (struct if_chain *chain)
+{
+  edge true_edge, false_edge;
+  int false_prob;
+  int flags_mask = ~(EDGE_FALLTHRU|EDGE_TRUE_VALUE|EDGE_FALSE_VALUE);
+
+  /* We keep these 2 edges, and remove the rest.  We need this specific
+     false_edge, because a phi in chain->last->dest might reference (the index
+     of) this edge.  For true_edge, we could pick any of them.  */
+  true_edge = BB_IFSC_TRUE_EDGE (chain->first);
+  false_edge = BB_IFSC_FALSE_EDGE (chain->last);
+
+  /* Update true edge.  */
+  true_edge->flags &= flags_mask;
+
+  /* Update false edge.  */
+  redirect_edge_pred (false_edge, chain->first);
+  false_edge->flags &= flags_mask;
+
+  false_prob = REG_BR_PROB_BASE;
+  remove_redundant_bbs_and_edges (chain, &false_prob);
+
+  /* Repair probabilities.  */
+  true_edge->probability = REG_BR_PROB_BASE - false_prob;
+  false_edge->probability = false_prob;
+
+  /* Force recalculation of dominance info.  */
+  free_dominance_info (CDI_DOMINATORS);
+  free_dominance_info (CDI_POST_DOMINATORS);
+}
+
+/* Create switch statement.  Borrows from gimplify_switch_expr.  */
+
+static void
+convert_if_chain_to_switch (struct if_chain *chain)
+{
+  tree label_decl_true, label_decl_false;
+  gimple label_true, label_false, gimple_switch;
+  gimple_stmt_iterator gsi;
+  tree default_case, other_case, constant;
+  unsigned int ix;
+  VEC (tree, heap) *labels;
+
+  labels = VEC_alloc (tree, heap, 8);
+
+  /* Create and insert true jump label.  */
+  label_decl_true = create_artificial_label (UNKNOWN_LOCATION);
+  label_true = gimple_build_label (label_decl_true);
+  gsi = gsi_start_bb (chain->true_dest);
+  gsi_insert_before (&gsi, label_true, GSI_SAME_STMT);
+
+  /* Create and insert false jump label.  */
+  label_decl_false = create_artificial_label (UNKNOWN_LOCATION);
+  label_false = gimple_build_label (label_decl_false);
+  gsi = gsi_start_bb (BB_IFSC_FALSE_EDGE (chain->last)->dest);
+  gsi_insert_before (&gsi, label_false, GSI_SAME_STMT);
+
+  /* Create default case label.  */
+  default_case = build3 (CASE_LABEL_EXPR, void_type_node,
+                         NULL_TREE, NULL_TREE,
+                         label_decl_false);
+
+  /* Create case labels.  */
+  for (ix = 0; VEC_iterate (tree, chain->unique_constants, ix, constant); ix++)
+    {
+      /* TODO: use ranges, as in gimplify_switch_expr.  */
+      other_case = build3 (CASE_LABEL_EXPR, void_type_node,
+                           constant, NULL_TREE,
+                           label_decl_true);
+      VEC_safe_push (tree, heap, labels, other_case);
+    }
+
+  /* Create and insert switch.  */
+  gimple_switch = gimple_build_switch_vec (chain->var, default_case, labels);
+  gsi = gsi_for_stmt (last_stmt (chain->first));
+  gsi_insert_before (&gsi, gimple_switch, GSI_SAME_STMT);
+
+  /* Remove now obsolete if.  */
+  gsi_remove (&gsi, true);
+
+  VEC_free (tree, heap, labels);
+}
+
+/* Allocation and initialization.  */
+
+static void
+init_pass (struct if_chain *chain)
+{
+  alloc_aux_for_blocks (sizeof (struct ifsc_info));
+
+  chain->constants = VEC_alloc (tree, heap, 8);
+  chain->unique_constants = VEC_alloc (tree, heap, 8);
+}
+
+/* Deallocation.  */
+
+static void
+finish_pass (struct if_chain *chain)
+{
+  free_aux_for_blocks ();
+
+  VEC_free (tree, heap, chain->constants);
+  VEC_free (tree, heap, chain->unique_constants);
+}
+
+/* Find if-chains and convert them to switches.  */
+
+static unsigned int
+do_if_to_switch (void)
+{
+  basic_block bb;
+  struct if_chain chain;
+  unsigned int convert_threshold = PARAM_VALUE (PARAM_IF_TO_SWITCH_THRESHOLD);
+
+  init_pass (&chain);
+
+  for (bb = cfun->cfg->x_entry_block_ptr->next_bb;
+       bb != cfun->cfg->x_exit_block_ptr;)
+    {
+      grow_if_chain (bb, &chain);
+
+      do
+        bb = bb->next_bb;
+      while (BB_IFSC_CHAINED (bb));
+
+      /* Determine if the chain is long enough.  */
+      if (VEC_length (tree, chain.unique_constants) < convert_threshold)
+        continue;
+
+      dump_if_chain (&chain);
+
+      convert_if_chain_to_switch (&chain);
+
+      update_cfg (&chain);
+    }
+
+  finish_pass (&chain);
+
+  return 0;
+}
+
+/* The pass gate.  */
+
+static bool
+if_to_switch_gate (void)
+{
+  return flag_tree_if_to_switch_conversion;
+}
+
+/* The pass definition.  */
+
+struct gimple_opt_pass pass_if_to_switch =
+{
+ {
+  GIMPLE_PASS,
+  "iftoswitch",                         /* name */
+  if_to_switch_gate,                    /* gate */
+  do_if_to_switch,                      /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_TREE_SWITCH_CONVERSION,            /* tv_id */
+  PROP_cfg | PROP_ssa,                  /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_update_ssa | TODO_dump_func
+  | TODO_ggc_collect | TODO_verify_ssa  /* todo_flags_finish */
+ }
+};
Index: gcc-4_5-branch/gcc/tree-pass.h
===================================================================
--- gcc-4_5-branch.orig/gcc/tree-pass.h
+++ gcc-4_5-branch/gcc/tree-pass.h
@@ -560,6 +560,7 @@ extern struct gimple_opt_pass pass_inlin
 extern struct gimple_opt_pass pass_all_early_optimizations;
 extern struct gimple_opt_pass pass_update_address_taken;
 extern struct gimple_opt_pass pass_convert_switch;
+extern struct gimple_opt_pass pass_if_to_switch;
 
 /* The root of the compilation pass tree, once constructed.  */
 extern struct opt_pass *all_passes, *all_small_ipa_passes, *all_lowering_passes,
