On 9/28/21 22:39, Andrew MacLeod wrote:
In Theory, modifying the IL should be fine, it happens already in places, but
its not extensively tested under those conditions yet.
Hello Andrew.
I've just tried using a global gimple_ranger and it crashes when loop
unswitching duplicates
some BBs.
Please try the attached patch for:
$ ./xgcc -B.
/home/marxin/Programming/gcc/gcc/testsuite/gcc.dg/loop-unswitch-1.c -O3 -c
during GIMPLE pass: unswitch
/home/marxin/Programming/gcc/gcc/testsuite/gcc.dg/loop-unswitch-1.c: In
function ‘xml_colorize_line’:
/home/marxin/Programming/gcc/gcc/testsuite/gcc.dg/loop-unswitch-1.c:6:6:
internal compiler error: in get_bb_range, at gimple-range-cache.cc:255
6 | void xml_colorize_line(unsigned int *p, int state)
| ^~~~~~~~~~~~~~~~~
0x871fcf sbr_vector::get_bb_range(irange&, basic_block_def const*)
/home/marxin/Programming/gcc/gcc/gimple-range-cache.cc:255
0x871fcf sbr_vector::get_bb_range(irange&, basic_block_def const*)
/home/marxin/Programming/gcc/gcc/gimple-range-cache.cc:253
0x1b99800 ranger_cache::fill_block_cache(tree_node*, basic_block_def*,
basic_block_def*)
/home/marxin/Programming/gcc/gcc/gimple-range-cache.cc:1332
0x1b99bc4 ranger_cache::block_range(irange&, basic_block_def*, tree_node*, bool)
/home/marxin/Programming/gcc/gcc/gimple-range-cache.cc:1107
0x1b9461c gimple_ranger::range_on_entry(irange&, basic_block_def*, tree_node*)
/home/marxin/Programming/gcc/gcc/gimple-range.cc:144
0x1b95140 gimple_ranger::range_of_expr(irange&, tree_node*, gimple*)
/home/marxin/Programming/gcc/gcc/gimple-range.cc:118
0x1b9ebef fold_using_range::range_of_range_op(irange&, gimple*, fur_source&)
/home/marxin/Programming/gcc/gcc/gimple-range-fold.cc:600
0x1ba0221 fold_using_range::fold_stmt(irange&, gimple*, fur_source&, tree_node*)
/home/marxin/Programming/gcc/gcc/gimple-range-fold.cc:552
0x1b94a16 gimple_ranger::fold_range_internal(irange&, gimple*, tree_node*)
/home/marxin/Programming/gcc/gcc/gimple-range.cc:243
0x1b94bab gimple_ranger::range_of_stmt(irange&, gimple*, tree_node*)
/home/marxin/Programming/gcc/gcc/gimple-range.cc:273
0x10a5b5f tree_unswitch_single_loop
/home/marxin/Programming/gcc/gcc/tree-ssa-loop-unswitch.c:390
0x10a5d62 tree_unswitch_single_loop
/home/marxin/Programming/gcc/gcc/tree-ssa-loop-unswitch.c:546
0x10a68fe tree_ssa_unswitch_loops()
/home/marxin/Programming/gcc/gcc/tree-ssa-loop-unswitch.c:106
Martin
diff --git a/gcc/testsuite/gcc.dg/loop-unswitch-6.c b/gcc/testsuite/gcc.dg/loop-unswitch-6.c
new file mode 100644
index 00000000000..8a022e0f200
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/loop-unswitch-6.c
@@ -0,0 +1,56 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funswitch-loops -fdump-tree-unswitch-details --param=max-unswitch-insns=1000 --param=max-unswitch-level=10" } */
+
+int
+__attribute__((noipa))
+foo(double *a, double *b, double *c, double *d, double *r, int size, int order)
+{
+ for (int i = 0; i < size; i++)
+ {
+ double tmp, tmp2;
+
+ switch(order)
+ {
+ case 0:
+ tmp = -8 * a[i];
+ tmp2 = 2 * b[i];
+ break;
+ case 1:
+ tmp = 3 * a[i] - 2 * b[i];
+ tmp2 = 5 * b[i] - 2 * c[i];
+ break;
+ case 2:
+ tmp = 9 * a[i] + 2 * b[i] + c[i];
+ tmp2 = 4 * b[i] + 2 * c[i] + 8 * d[i];
+ break;
+ case 3:
+ tmp = 3 * a[i] + 2 * b[i] - c[i];
+ tmp2 = b[i] - 2 * c[i] + 8 * d[i];
+ break;
+ defaut:
+ __builtin_unreachable ();
+ }
+
+ double x = 3 * tmp + d[i] + tmp;
+ double y = 3.4f * tmp + d[i] + tmp2;
+ r[i] = x + y;
+ }
+
+ return 0;
+}
+
+#define N 16 * 1024
+double aa[N], bb[N], cc[N], dd[N], rr[N];
+
+int main()
+{
+ for (int i = 0; i < 100 * 1000; i++)
+ foo (aa, bb, cc, dd, rr, N, i % 4);
+}
+
+
+/* Test that we actually unswitched something. */
+/* { dg-final { scan-tree-dump ";; Unswitching loop with condition: order.* == 0" "unswitch" } } */
+/* { dg-final { scan-tree-dump ";; Unswitching loop with condition: order.* == 1" "unswitch" } } */
+/* { dg-final { scan-tree-dump ";; Unswitching loop with condition: order.* == 2" "unswitch" } } */
+/* { dg-final { scan-tree-dump ";; Unswitching loop with condition: order.* == 3" "unswitch" } } */
diff --git a/gcc/testsuite/gcc.dg/loop-unswitch-7.c b/gcc/testsuite/gcc.dg/loop-unswitch-7.c
new file mode 100644
index 00000000000..00f2fcff64b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/loop-unswitch-7.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funswitch-loops -fdump-tree-unswitch-details --param=max-unswitch-insns=1000 --param=max-unswitch-level=10" } */
+
+int
+foo(double *a, double *b, double *c, double *d, double *r, int size, int order)
+{
+ for (int i = 0; i < size; i++)
+ {
+ double tmp, tmp2;
+
+ switch(order)
+ {
+ case 5 ... 6:
+ case 9:
+ tmp = -8 * a[i];
+ tmp2 = 2 * b[i];
+ break;
+ case 11:
+ tmp = 3 * a[i] - 2 * b[i];
+ tmp2 = 5 * b[i] - 2 * c[i];
+ break;
+ case 22:
+ tmp = 9 * a[i] + 2 * b[i] + c[i];
+ tmp2 = 4 * b[i] + 2 * c[i] + 8 * d[i];
+ break;
+ case 33:
+ tmp = 3 * a[i] + 2 * b[i] - c[i];
+ tmp2 = b[i] - 2 * c[i] + 8 * d[i];
+ break;
+ defaut:
+ __builtin_unreachable ();
+ }
+
+ double x = 3 * tmp + d[i] + tmp;
+ double y = 3.4f * tmp + d[i] + tmp2;
+ r[i] = x + y;
+ }
+
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump ";; Unswitching loop with condition: order_.* >= 5 & order_.* <= 6 | order_.* == 9" "unswitch" } } */
+/* { dg-final { scan-tree-dump ";; Unswitching loop with condition: order.* == 1" "unswitch" } } */
+/* { dg-final { scan-tree-dump ";; Unswitching loop with condition: order.* == 2" "unswitch" } } */
+/* { dg-final { scan-tree-dump ";; Unswitching loop with condition: order.* == 3" "unswitch" } } */
diff --git a/gcc/testsuite/gcc.dg/loop-unswitch-8.c b/gcc/testsuite/gcc.dg/loop-unswitch-8.c
new file mode 100644
index 00000000000..4cec1f53bcc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/loop-unswitch-8.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funswitch-loops -fdump-tree-unswitch-details" } */
+
+int
+foo(double *a, double *b, double *c, double *d, double *r, int size, int order)
+{
+ for (int i = 0; i < size; i++)
+ {
+ double tmp;
+
+ if (order == 1)
+ tmp = -8 * a[i];
+ else
+ tmp = -4 * b[i];
+
+ double x = 3 * tmp + d[i] + tmp;
+
+ if (order == 1)
+ x += 2;
+
+ double y = 3.4f * tmp + d[i];
+ r[i] = x + y;
+ }
+
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-times ";; Unswitching loop with condition: order.* == 1" 1 "unswitch" } } */
diff --git a/gcc/testsuite/gcc.dg/loop-unswitch-9.c b/gcc/testsuite/gcc.dg/loop-unswitch-9.c
new file mode 100644
index 00000000000..3058d4a5b38
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/loop-unswitch-9.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funswitch-loops -fdump-tree-unswitch-details" } */
+
+int
+foo(double *a, double *b, double *c, double *d, double *r, int size, unsigned order)
+{
+ for (int i = 0; i < size; i++)
+ {
+ double tmp;
+
+ switch (order)
+ {
+ case 0 ... 4:
+ tmp = -8 * a[i];
+ break;
+ default:
+ tmp = -4 * b[i];
+ break;
+ }
+
+ double x = 3 * tmp + d[i] + tmp;
+
+ /* This should not be unswitched as it's mutually excluded with case 0 ... 4. */
+ if (order >= 5)
+ x += 2;
+
+ double y = 3.4f * tmp + d[i];
+ r[i] = x + y;
+ }
+
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-times ";; Unswitching loop with condition: order.* <= 4" 1 "unswitch" } } */
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index 8ed8c69b5b1..7db54f1cda2 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -9062,11 +9062,16 @@ gimple_lv_add_condition_to_bb (basic_block first_head ATTRIBUTE_UNUSED,
edge e0;
/* Build new conditional expr */
+ gsi = gsi_last_bb (cond_bb);
+
+ cond_expr = force_gimple_operand_gsi_1 (&gsi, cond_expr,
+ is_gimple_condexpr_for_cond,
+ NULL_TREE, false,
+ GSI_CONTINUE_LINKING);
new_cond_expr = gimple_build_cond_from_tree (cond_expr,
NULL_TREE, NULL_TREE);
/* Add new cond in cond_bb. */
- gsi = gsi_last_bb (cond_bb);
gsi_insert_after (&gsi, new_cond_expr, GSI_NEW_STMT);
/* Adjust edges appropriately to connect new head with first head
diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c
index fe4dacc0833..41261efe404 100644
--- a/gcc/tree-ssa-loop-unswitch.c
+++ b/gcc/tree-ssa-loop-unswitch.c
@@ -37,6 +37,10 @@ along with GCC; see the file COPYING3. If not see
#include "gimple-iterator.h"
#include "cfghooks.h"
#include "tree-ssa-loop-manip.h"
+#include "cfganal.h"
+#include "tree-cfgcleanup.h"
+#include "tree-pretty-print.h"
+#include "gimple-range.h"
/* This file implements the loop unswitching, i.e. transformation of loops like
@@ -74,9 +78,9 @@ along with GCC; see the file COPYING3. If not see
tree-ssa-loop-im.c ensures that all the suitable conditions are in this
shape. */
-static class loop *tree_unswitch_loop (class loop *, basic_block, tree);
-static bool tree_unswitch_single_loop (class loop *, int);
-static tree tree_may_unswitch_on (basic_block, class loop *);
+static class loop *tree_unswitch_loop (class loop *, basic_block, tree, edge);
+static bool tree_unswitch_single_loop (class loop *, gimple_ranger *ranger, int);
+static tree tree_may_unswitch_on (basic_block, class loop *, edge *);
static bool tree_unswitch_outer_loop (class loop *);
static edge find_loop_guard (class loop *);
static bool empty_bb_without_guard_p (class loop *, basic_block);
@@ -84,6 +88,7 @@ static bool used_outside_loop_p (class loop *, tree);
static void hoist_guard (class loop *, edge);
static bool check_exit_phi (class loop *);
static tree get_vop_from_header (class loop *);
+static void clean_up_switches (void);
/* Main entry point. Perform loop unswitching on all suitable loops. */
@@ -91,19 +96,24 @@ unsigned int
tree_ssa_unswitch_loops (void)
{
bool changed = false;
+ gimple_ranger *ranger = enable_ranger (cfun);
/* Go through all loops starting from innermost. */
for (auto loop : loops_list (cfun, LI_FROM_INNERMOST))
{
if (!loop->inner)
/* Unswitch innermost loop. */
- changed |= tree_unswitch_single_loop (loop, 0);
+ changed |= tree_unswitch_single_loop (loop, ranger, 0);
else
changed |= tree_unswitch_outer_loop (loop);
}
+ disable_ranger (cfun);
if (changed)
- return TODO_cleanup_cfg;
+ {
+ clean_up_switches ();
+ return TODO_cleanup_cfg;
+ }
return 0;
}
@@ -182,80 +192,114 @@ is_maybe_undefined (const tree name, gimple *stmt, class loop *loop)
}
/* Checks whether we can unswitch LOOP on condition at end of BB -- one of its
- basic blocks (for what it means see comments below). */
+ basic blocks (for what it means see comments below).
+ When a gswitch case is returned, fill up COND_EDGE for it. */
static tree
-tree_may_unswitch_on (basic_block bb, class loop *loop)
+tree_may_unswitch_on (basic_block bb, class loop *loop, edge *cond_edge)
{
gimple *last, *def;
- gcond *stmt;
- tree cond, use;
+ tree use;
basic_block def_bb;
ssa_op_iter iter;
/* BB must end in a simple conditional jump. */
last = last_stmt (bb);
- if (!last || gimple_code (last) != GIMPLE_COND)
- return NULL_TREE;
- stmt = as_a <gcond *> (last);
-
- /* To keep the things simple, we do not directly remove the conditions,
- but just replace tests with 0 != 0 resp. 1 != 0. Prevent the infinite
- loop where we would unswitch again on such a condition. */
- if (gimple_cond_true_p (stmt) || gimple_cond_false_p (stmt))
- return NULL_TREE;
-
- /* Condition must be invariant. */
- FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+ if (gcond *stmt = safe_dyn_cast <gcond *> (last))
+ {
+ /* To keep the things simple, we do not directly remove the conditions,
+ but just replace tests with 0 != 0 resp. 1 != 0. Prevent the infinite
+ loop where we would unswitch again on such a condition. */
+ if (gimple_cond_true_p (stmt) || gimple_cond_false_p (stmt))
+ return NULL_TREE;
+
+ /* Condition must be invariant. */
+ FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+ {
+ def = SSA_NAME_DEF_STMT (use);
+ def_bb = gimple_bb (def);
+ if (def_bb
+ && flow_bb_inside_loop_p (loop, def_bb))
+ return NULL_TREE;
+ /* Unswitching on undefined values would introduce undefined
+ behavior that the original program might never exercise. */
+ if (is_maybe_undefined (use, stmt, loop))
+ return NULL_TREE;
+ }
+
+ edge e1 = EDGE_SUCC (bb, 0);
+ edge e2 = EDGE_SUCC (bb, 1);
+ *cond_edge = e1->flags & EDGE_TRUE_VALUE ? e1 : e2;
+ return build2 (gimple_cond_code (stmt), boolean_type_node,
+ gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
+ }
+ else if (gswitch *stmt = safe_dyn_cast <gswitch *> (last))
{
- def = SSA_NAME_DEF_STMT (use);
+ unsigned nlabels = gimple_switch_num_labels (stmt);
+ tree idx = gimple_switch_index (stmt);
+ if (TREE_CODE (idx) != SSA_NAME
+ || nlabels < 1)
+ return NULL_TREE;
+ /* Index must be invariant. */
+ def = SSA_NAME_DEF_STMT (idx);
def_bb = gimple_bb (def);
if (def_bb
&& flow_bb_inside_loop_p (loop, def_bb))
return NULL_TREE;
/* Unswitching on undefined values would introduce undefined
behavior that the original program might never exercise. */
- if (is_maybe_undefined (use, stmt, loop))
+ if (is_maybe_undefined (idx, stmt, loop))
return NULL_TREE;
- }
-
- cond = build2 (gimple_cond_code (stmt), boolean_type_node,
- gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
-
- return cond;
-}
-
-/* Simplifies COND using checks in front of the entry of the LOOP. Just very
- simplish (sufficient to prevent us from duplicating loop in unswitching
- unnecessarily). */
-static tree
-simplify_using_entry_checks (class loop *loop, tree cond)
-{
- edge e = loop_preheader_edge (loop);
- gimple *stmt;
+ edge e;
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->succs)
+ {
+ if (!(e->flags & EDGE_IGNORE))
+ {
+ /* Build compound expression for all cases leading
+ to this edge. */
+ tree expr = NULL_TREE;
+ for (unsigned i = 1; i < gimple_switch_num_labels (stmt); ++i)
+ {
+ tree lab = gimple_switch_label (stmt, i);
+ basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+ edge e2 = find_edge (gimple_bb (stmt), dest);
+ if (e == e2)
+ {
+ tree cmp;
+ if (CASE_HIGH (lab) != NULL_TREE)
+ {
+ tree cmp1 = build2 (GE_EXPR, boolean_type_node, idx,
+ CASE_LOW (lab));
+ tree cmp2 = build2 (LE_EXPR, boolean_type_node, idx,
+ CASE_HIGH (lab));
+ cmp = build2 (BIT_AND_EXPR, boolean_type_node, cmp1,
+ cmp2);
+ }
+ else
+ cmp = build2 (EQ_EXPR, boolean_type_node, idx,
+ CASE_LOW (lab));
+
+ /* Combine the expression with the existing one. */
+ if (expr == NULL_TREE)
+ expr = cmp;
+ else
+ expr = build2 (BIT_IOR_EXPR, boolean_type_node, expr,
+ cmp);
+ }
+ }
- while (1)
- {
- stmt = last_stmt (e->src);
- if (stmt
- && gimple_code (stmt) == GIMPLE_COND
- && gimple_cond_code (stmt) == TREE_CODE (cond)
- && operand_equal_p (gimple_cond_lhs (stmt),
- TREE_OPERAND (cond, 0), 0)
- && operand_equal_p (gimple_cond_rhs (stmt),
- TREE_OPERAND (cond, 1), 0))
- return (e->flags & EDGE_TRUE_VALUE
- ? boolean_true_node
- : boolean_false_node);
-
- if (!single_pred_p (e->src))
- return cond;
-
- e = single_pred_edge (e->src);
- if (e->src == ENTRY_BLOCK_PTR_FOR_FN (cfun))
- return cond;
+ if (expr != NULL_TREE)
+ {
+ *cond_edge = e;
+ return expr;
+ }
+ }
+ }
}
+
+ return NULL_TREE;
}
/* Unswitch single LOOP. NUM is number of unswitchings done; we do not allow
@@ -263,12 +307,13 @@ simplify_using_entry_checks (class loop *loop, tree cond)
grow exponentially. */
static bool
-tree_unswitch_single_loop (class loop *loop, int num)
+tree_unswitch_single_loop (class loop *loop, gimple_ranger *ranger, int num)
{
basic_block *bbs;
class loop *nloop;
unsigned i, found;
tree cond = NULL_TREE;
+ edge cond_edge = NULL;
gimple *stmt;
bool changed = false;
HOST_WIDE_INT iterations;
@@ -315,7 +360,7 @@ tree_unswitch_single_loop (class loop *loop, int num)
{
/* Find a bb to unswitch on. */
for (; i < loop->num_nodes; i++)
- if ((cond = tree_may_unswitch_on (bbs[i], loop)))
+ if ((cond = tree_may_unswitch_on (bbs[i], loop, &cond_edge)))
break;
if (i == loop->num_nodes)
@@ -333,24 +378,56 @@ tree_unswitch_single_loop (class loop *loop, int num)
break;
}
- cond = simplify_using_entry_checks (loop, cond);
stmt = last_stmt (bbs[i]);
- if (integer_nonzerop (cond))
+ gcond *condition = dyn_cast<gcond *> (stmt);
+ gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
+ if (condition != NULL)
{
- /* Remove false path. */
- gimple_cond_set_condition_from_tree (as_a <gcond *> (stmt),
- boolean_true_node);
- changed = true;
+ tree result;
+ int_range_max r;
+
+ if (ranger->range_of_stmt (r, stmt) && r.singleton_p (&result))
+ {
+ gimple_cond_set_condition_from_tree (condition,
+ result);
+ update_stmt (condition);
+ changed = true;
+ }
}
- else if (integer_zerop (cond))
+ else if (swtch != NULL)
{
- /* Remove true path. */
- gimple_cond_set_condition_from_tree (as_a <gcond *> (stmt),
- boolean_false_node);
- changed = true;
+ hash_set<edge> ignored_edges;
+ unsigned nlabels = gimple_switch_num_labels (swtch);
+ tree index_candidate = NULL_TREE;
+
+ for (unsigned i = 0; i < nlabels; ++i)
+ {
+ tree lab = gimple_switch_label (swtch, i);
+ basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+ edge e = find_edge (gimple_bb (stmt), dest);
+
+ int_range_max r;
+ if (ranger->range_on_edge (r, e, gimple_switch_index (swtch))
+ && r.undefined_p ())
+ {
+ e->flags |= EDGE_IGNORE;
+ ignored_edges.add (e);
+ }
+ else
+ index_candidate = CASE_LOW (lab);
+ }
+
+ if (index_candidate != NULL_TREE
+ && ignored_edges.elements () == EDGE_COUNT (bbs[i]->succs) - 1)
+ {
+ gimple_switch_set_index (swtch, index_candidate);
+ update_stmt (swtch);
+ }
}
+
/* Do not unswitch too much. */
- else if (num > param_max_unswitch_level)
+ if (num > param_max_unswitch_level)
{
i++;
continue;
@@ -371,7 +448,6 @@ tree_unswitch_single_loop (class loop *loop, int num)
break;
}
- update_stmt (stmt);
i++;
}
@@ -435,7 +511,7 @@ tree_unswitch_single_loop (class loop *loop, int num)
/* Find a bb to unswitch on. */
for (; found < loop->num_nodes; found++)
if ((bbs[found]->flags & BB_REACHABLE)
- && (cond = tree_may_unswitch_on (bbs[found], loop)))
+ && (cond = tree_may_unswitch_on (bbs[found], loop, &cond_edge)))
break;
if (found == loop->num_nodes)
@@ -446,11 +522,15 @@ tree_unswitch_single_loop (class loop *loop, int num)
}
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, ";; Unswitching loop\n");
+ {
+ fprintf (dump_file, ";; Unswitching loop with condition: ");
+ print_generic_expr (dump_file, cond);
+ fprintf (dump_file, "\n");
+ }
initialize_original_copy_tables ();
/* Unswitch the loop on this condition. */
- nloop = tree_unswitch_loop (loop, bbs[found], cond);
+ nloop = tree_unswitch_loop (loop, bbs[found], cond, cond_edge);
if (!nloop)
{
free_original_copy_tables ();
@@ -463,8 +543,8 @@ tree_unswitch_single_loop (class loop *loop, int num)
free_original_copy_tables ();
/* Invoke itself on modified loops. */
- tree_unswitch_single_loop (nloop, num + 1);
- tree_unswitch_single_loop (loop, num + 1);
+ tree_unswitch_single_loop (nloop, ranger, num + 1);
+ tree_unswitch_single_loop (loop, ranger, num + 1);
free (bbs);
return true;
}
@@ -476,18 +556,15 @@ tree_unswitch_single_loop (class loop *loop, int num)
static class loop *
tree_unswitch_loop (class loop *loop,
- basic_block unswitch_on, tree cond)
+ basic_block unswitch_on, tree cond, edge cond_edge)
{
profile_probability prob_true;
- edge edge_true, edge_false;
/* Some sanity checking. */
gcc_assert (flow_bb_inside_loop_p (loop, unswitch_on));
- gcc_assert (EDGE_COUNT (unswitch_on->succs) == 2);
gcc_assert (loop->inner == NULL);
- extract_true_false_edges_from_block (unswitch_on, &edge_true, &edge_false);
- prob_true = edge_true->probability;
+ prob_true = cond_edge->probability;
return loop_version (loop, unshare_expr (cond),
NULL, prob_true,
prob_true.invert (),
@@ -965,6 +1042,42 @@ check_exit_phi (class loop *loop)
return true;
}
+/* Remove all dead cases from switches that are unswitched. */
+
+static void
+clean_up_switches (void)
+{
+ basic_block bb;
+
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ gimple *last = last_stmt (bb);
+ if (gswitch *stmt = safe_dyn_cast <gswitch *> (last))
+ {
+ unsigned nlabels = gimple_switch_num_labels (stmt);
+ unsigned index = 1;
+ for (unsigned i = 1; i < nlabels; ++i)
+ {
+ tree lab = gimple_switch_label (stmt, i);
+ basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+ edge e = find_edge (gimple_bb (stmt), dest);
+ if (e == NULL)
+ ; /* The edge is already removed. */
+ else if (e->flags & EDGE_IGNORE)
+ remove_edge (e);
+ else
+ {
+ gimple_switch_set_label (stmt, index, lab);
+ ++index;
+ }
+ }
+
+ if (index != nlabels)
+ gimple_switch_set_num_labels (stmt, index);
+ }
+ }
+}
+
/* Loop unswitching pass. */
namespace {