diff mbox

make find_taken_edge handle case with just default

Message ID alpine.LSU.2.20.1706291100160.23185@zhemvz.fhfr.qr
State New
Headers show

Commit Message

Richard Biener June 29, 2017, 9:03 a.m. UTC
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  <rguenther@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.
diff mbox

Patch

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)
     {