int old_size = gimple_switch_num_labels (stmt);
int i, next_index, new_size;
basic_block default_bb = NULL;
+ hash_set<tree> *removed_labels = NULL;
default_bb = gimple_switch_default_bb (cfun, stmt);
base_bb = label_to_block (cfun, CASE_LABEL (base_case));
/* Discard cases that have the same destination as the default case or
- whose destiniation blocks have already been removed as unreachable. */
- if (base_bb == NULL || base_bb == default_bb)
+ whose destination blocks have already been removed as unreachable. */
+ if (base_bb == NULL
+ || base_bb == default_bb
+ || (removed_labels
+ && removed_labels->contains (CASE_LABEL (base_case))))
{
i++;
continue;
/* Merge the cases if they jump to the same place,
and their ranges are consecutive. */
if (merge_bb == base_bb
+ && (removed_labels == NULL
+ || !removed_labels->contains (CASE_LABEL (merge_case)))
&& wi::to_wide (CASE_LOW (merge_case)) == bhp1)
{
- base_high = CASE_HIGH (merge_case) ?
- CASE_HIGH (merge_case) : CASE_LOW (merge_case);
+ base_high
+ = (CASE_HIGH (merge_case)
+ ? CASE_HIGH (merge_case) : CASE_LOW (merge_case));
CASE_HIGH (base_case) = base_high;
next_index++;
}
{
edge base_edge = find_edge (gimple_bb (stmt), base_bb);
if (base_edge != NULL)
- remove_edge_and_dominated_blocks (base_edge);
+ {
+ for (gimple_stmt_iterator gsi = gsi_start_bb (base_bb);
+ !gsi_end_p (gsi); gsi_next (&gsi))
+ if (glabel *stmt = dyn_cast <glabel *> (gsi_stmt (gsi)))
+ {
+ if (FORCED_LABEL (gimple_label_label (stmt))
+ || DECL_NONLOCAL (gimple_label_label (stmt)))
+ {
+ /* Forced/non-local labels aren't going to be removed,
+ but they will be moved to some neighbouring basic
+ block. If some later case label refers to one of
+ those labels, we should throw that case away rather
+ than keeping it around and refering to some random
+ other basic block without an edge to it. */
+ if (removed_labels == NULL)
+ removed_labels = new hash_set<tree>;
+ removed_labels->add (gimple_label_label (stmt));
+ }
+ }
+ else
+ break;
+ remove_edge_and_dominated_blocks (base_edge);
+ }
i = next_index;
continue;
}
if (new_size < old_size)
gimple_switch_set_num_labels (stmt, new_size);
+ delete removed_labels;
return new_size < old_size;
}