This refactors things a bit to make CFG cleanup handle switches with just a default label. If we make sure to cleanup the CFG after group_case_labels removes cases with just __builtin_unreachable () inside then this fixes the ICE seen in PR81994 as well.
I wonder if find_taken_edge should generally handle successors with __builtin_unreachable () -- OTOH that would get rid of those too early I guess. Bootstrap / regtest running on x86_64-unknown-linux-gnu. Richard. 2017-06-29 Richard Biener <rguent...@suse.de> * tree-cfg.c (group_case_labels_stmt): Return whether we changed anything. (group_case_labels): Likewise. (find_taken_edge): Push sanity checking on val to workers... (find_taken_edge_cond_expr): ... here (find_taken_edge_switch_expr): ... and here, handle cases with just a default label. * tree-cfg.h (group_case_labels_stmt): Adjust prototype. (group_case_labels): Likewise. * tree-cfgcleanup.c (execute_cleanup_cfg_post_optimizing): When group_case_labels does anything cleanup the CFG again. Index: gcc/tree-cfg.c =================================================================== --- gcc/tree-cfg.c (revision 249769) +++ gcc/tree-cfg.c (working copy) @@ -1675,7 +1675,7 @@ cleanup_dead_labels (void) the ones jumping to the same label. Eg. three separate entries 1: 2: 3: become one entry 1..3: */ -void +bool group_case_labels_stmt (gswitch *stmt) { int old_size = gimple_switch_num_labels (stmt); @@ -1759,23 +1759,27 @@ group_case_labels_stmt (gswitch *stmt) gcc_assert (new_size <= old_size); gimple_switch_set_num_labels (stmt, new_size); + return new_size < old_size; } /* Look for blocks ending in a multiway branch (a GIMPLE_SWITCH), and scan the sorted vector of cases. Combine the ones jumping to the same label. */ -void +bool group_case_labels (void) { basic_block bb; + bool changed = false; FOR_EACH_BB_FN (bb, cfun) { gimple *stmt = last_stmt (bb); if (stmt && gimple_code (stmt) == GIMPLE_SWITCH) - group_case_labels_stmt (as_a <gswitch *> (stmt)); + changed |= group_case_labels_stmt (as_a <gswitch *> (stmt)); } + + return changed; } /* Checks whether we can merge block B into block A. */ @@ -2243,15 +2247,8 @@ find_taken_edge (basic_block bb, tree va stmt = last_stmt (bb); - gcc_assert (stmt); gcc_assert (is_ctrl_stmt (stmt)); - if (val == NULL) - return NULL; - - if (!is_gimple_min_invariant (val)) - return NULL; - if (gimple_code (stmt) == GIMPLE_COND) return find_taken_edge_cond_expr (bb, val); @@ -2266,7 +2263,8 @@ find_taken_edge (basic_block bb, tree va It may be the case that we only need to allow the LABEL_REF to appear inside an ADDR_EXPR, but we also allow the LABEL_REF to appear inside a LABEL_EXPR just to be safe. */ - if ((TREE_CODE (val) == ADDR_EXPR || TREE_CODE (val) == LABEL_EXPR) + if (val + && (TREE_CODE (val) == ADDR_EXPR || TREE_CODE (val) == LABEL_EXPR) && TREE_CODE (TREE_OPERAND (val, 0)) == LABEL_DECL) return find_taken_edge_computed_goto (bb, TREE_OPERAND (val, 0)); return NULL; @@ -2304,9 +2302,12 @@ find_taken_edge_cond_expr (basic_block b { edge true_edge, false_edge; + if (val == NULL + || TREE_CODE (val) != INTEGER_CST) + return NULL; + extract_true_false_edges_from_block (bb, &true_edge, &false_edge); - gcc_assert (TREE_CODE (val) == INTEGER_CST); return (integer_zerop (val) ? false_edge : true_edge); } @@ -2322,7 +2323,12 @@ find_taken_edge_switch_expr (gswitch *sw edge e; tree taken_case; - taken_case = find_case_label_for_value (switch_stmt, val); + if (gimple_switch_num_labels (switch_stmt) == 1) + taken_case = gimple_switch_default_label (switch_stmt); + else if (! val || TREE_CODE (val) != INTEGER_CST) + return NULL; + else + taken_case = find_case_label_for_value (switch_stmt, val); dest_bb = label_to_block (CASE_LABEL (taken_case)); e = find_edge (bb, dest_bb); Index: gcc/tree-cfg.h =================================================================== --- gcc/tree-cfg.h (revision 249769) +++ gcc/tree-cfg.h (working copy) @@ -36,8 +36,8 @@ extern void end_recording_case_labels (v extern basic_block label_to_block_fn (struct function *, tree); #define label_to_block(t) (label_to_block_fn (cfun, t)) extern void cleanup_dead_labels (void); -extern void group_case_labels_stmt (gswitch *); -extern void group_case_labels (void); +extern bool group_case_labels_stmt (gswitch *); +extern bool group_case_labels (void); extern void replace_uses_by (tree, tree); extern basic_block single_noncomplex_succ (basic_block bb); extern void notice_special_calls (gcall *); Index: gcc/tree-cfgcleanup.c =================================================================== --- gcc/tree-cfgcleanup.c (revision 249769) +++ gcc/tree-cfgcleanup.c (working copy) @@ -1205,7 +1205,8 @@ execute_cleanup_cfg_post_optimizing (voi } maybe_remove_unreachable_handlers (); cleanup_dead_labels (); - group_case_labels (); + if (group_case_labels ()) + todo |= TODO_cleanup_cfg; if ((flag_compare_debug_opt || flag_compare_debug) && flag_dump_final_insns) {