This patch is the second attempt to fix PR51513, namely the generation of
wild branches due to switch case statements that only contain calls to
__builtin_unreachable(). With the first attempt:
https://gcc.gnu.org/ml/gcc-patches/2016-04/msg01915.html
richi said he preferred if we just eliminated the range check for
default case statements that contained __builtin_unreachable().
This patch implements that idea. It also removes normal case
statement blocks that are marked as unreachable, but in those cases,
we just use a dummy label in the jump table for them.
This passed bootstrap and regtesting with no regressions on powerpc64-linux
and x86_64-linux. Ok for trunk now or trunk during stage1?
Peter
gcc/
* tree-cfg.c (gimple_unreachable_bb_p): New function.
(assert_unreachable_fallthru_edge_p): Use it.
* tree-cfg.h: Prototype it.
* stmt.c: Include cfghooks.h and tree-cfg.h.
(emit_case_dispatch_table) <gap_label>: New local variable.
Use it to fill dispatch table gaps and unreachable cases.
Remove edges to unreachable blocks.
(expand_case): Test for unreachable default case statement and
remove its edge. Set default_label accordingly.
(emit_case_nodes): Only emit branch to default_label if it
exists.
gcc/testsuite/
* gcc.target/powerpc/pr51513.c: New test.
Index: gcc/stmt.c
===================================================================
--- gcc/stmt.c (revision 246661)
+++ gcc/stmt.c (working copy)
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.
#include "rtl.h"
#include "tree.h"
#include "gimple.h"
+#include "cfghooks.h"
#include "predict.h"
#include "alloc-pool.h"
#include "memmodel.h"
@@ -49,6 +50,7 @@ along with GCC; see the file COPYING3.
#include "expr.h"
#include "langhooks.h"
#include "cfganal.h"
+#include "tree-cfg.h"
#include "params.h"
#include "dumpfile.h"
#include "builtins.h"
@@ -989,6 +991,14 @@ emit_case_dispatch_table (tree index_exp
labelvec = XALLOCAVEC (rtx, ncases);
memset (labelvec, 0, ncases * sizeof (rtx));
+ /* The dispatch table may contain gaps and labels of unreachable case
+ statements. Gaps can exist at the beginning of the table if we tried
+ to avoid the minval subtraction. We fill the dispatch table slots
+ associated with the gaps and unreachable case blocks with the default
+ case label. However, in the event the default case itself is unreachable,
+ we then use any label from one of the reachable case statements. */
+ rtx gap_label = (default_label) ? default_label : fallback_label;
+
for (n = case_list; n; n = n->right)
{
/* Compute the low and high bounds relative to the minimum
@@ -1000,42 +1010,51 @@ emit_case_dispatch_table (tree index_exp
HOST_WIDE_INT i_high
= tree_to_uhwi (fold_build2 (MINUS_EXPR, index_type,
n->high, minval));
- HOST_WIDE_INT i;
+ basic_block case_bb = label_to_block (n->code_label);
+ rtx case_label;
+ if (gimple_unreachable_bb_p (case_bb))
+ {
+ /* We have an unreachable case statement, so replace its label
+ with a dummy label and remove the edge to the unreachable block.
+ The block itself will be automatically removed later. */
+ case_label = gap_label;
+ remove_edge (find_edge (stmt_bb, case_bb));
+ }
+ else
+ case_label = label_rtx (n->code_label);
+
+ HOST_WIDE_INT i;
for (i = i_low; i <= i_high; i ++)
- labelvec[i]
- = gen_rtx_LABEL_REF (Pmode, label_rtx (n->code_label));
+ labelvec[i] = gen_rtx_LABEL_REF (Pmode, case_label);
}
- /* Fill in the gaps with the default. We may have gaps at
- the beginning if we tried to avoid the minval subtraction,
- so substitute some label even if the default label was
- deemed unreachable. */
- if (!default_label)
- default_label = fallback_label;
+ /* Now fill in the dispatch table gaps. */
for (i = 0; i < ncases; i++)
if (labelvec[i] == 0)
{
- has_gaps = true;
- labelvec[i] = gen_rtx_LABEL_REF (Pmode, default_label);
+ has_gaps = true;
+ labelvec[i] = gen_rtx_LABEL_REF (Pmode, gap_label);
}
- if (has_gaps)
- {
- /* There is at least one entry in the jump table that jumps
- to default label. The default label can either be reached
- through the indirect jump or the direct conditional jump
- before that. Split the probability of reaching the
- default label among these two jumps. */
- new_default_prob = conditional_probability (default_prob/2,
- base);
- default_prob /= 2;
- base -= default_prob;
- }
- else
+ if (default_label)
{
- base -= default_prob;
- default_prob = 0;
+ if (has_gaps)
+ {
+ /* There is at least one entry in the jump table that jumps
+ to default label. The default label can either be reached
+ through the indirect jump or the direct conditional jump
+ before that. Split the probability of reaching the
+ default label among these two jumps. */
+ new_default_prob = conditional_probability (default_prob/2, base);
+ default_prob /= 2;
+ base -= default_prob;
+ }
+ else
+ {
+ base -= default_prob;
+ default_prob = 0;
+ }
}
if (default_edge)
@@ -1115,7 +1134,8 @@ void
expand_case (gswitch *stmt)
{
tree minval = NULL_TREE, maxval = NULL_TREE, range = NULL_TREE;
- rtx_code_label *default_label = NULL;
+ rtx_code_label *default_label;
+ int default_prob;
unsigned int count, uniq;
int i;
int ncases = gimple_switch_num_labels (stmt);
@@ -1139,15 +1159,24 @@ expand_case (gswitch *stmt)
/* cleanup_tree_cfg removes all SWITCH_EXPR with their index
expressions being INTEGER_CST. */
gcc_assert (TREE_CODE (index_expr) != INTEGER_CST);
-
do_pending_stack_adjust ();
/* Find the default case target label. */
- default_label = jump_target_rtx
- (CASE_LABEL (gimple_switch_default_label (stmt)));
- edge default_edge = EDGE_SUCC (bb, 0);
- int default_prob = default_edge->probability;
+ tree default_tree = CASE_LABEL (gimple_switch_default_label (stmt));
+ basic_block default_bb = label_to_block (default_tree);
+ if (gimple_unreachable_bb_p (default_bb))
+ {
+ default_label = NULL;
+ default_prob = 0;
+ remove_edge (find_edge (bb, default_bb));
+ }
+ else
+ {
+ default_label = jump_target_rtx (default_tree);
+ edge default_edge = EDGE_SUCC (bb, 0);
+ default_prob = default_edge->probability;
+ }
/* Get upper and lower bounds of case values. */
elt = gimple_switch_label (stmt, 1);
@@ -1725,7 +1754,7 @@ emit_case_nodes (rtx index, case_node_pt
if (node->right->right || node->right->left
|| !tree_int_cst_equal (node->right->low, node->right->high))
{
- if (!node_has_low_bound (node, index_type))
+ if (default_label && !node_has_low_bound (node, index_type))
{
probability = conditional_probability (
default_prob/2,
@@ -1767,7 +1796,7 @@ emit_case_nodes (rtx index, case_node_pt
if (node->left->left || node->left->right
|| !tree_int_cst_equal (node->left->low, node->left->high))
{
- if (!node_has_high_bound (node, index_type))
+ if (default_label && !node_has_high_bound (node, index_type))
{
probability = conditional_probability (
default_prob/2,
@@ -1891,7 +1920,7 @@ emit_case_nodes (rtx index, case_node_pt
{
/* Deal with values to the left of this node,
if they are possible. */
- if (!node_has_low_bound (node, index_type))
+ if (default_label && !node_has_low_bound (node, index_type))
{
probability = conditional_probability (
default_prob/2,
@@ -1928,7 +1957,7 @@ emit_case_nodes (rtx index, case_node_pt
{
/* Deal with values to the right of this node,
if they are possible. */
- if (!node_has_high_bound (node, index_type))
+ if (default_label && !node_has_high_bound (node, index_type))
{
probability = conditional_probability (
default_prob/2,
@@ -1969,7 +1998,7 @@ emit_case_nodes (rtx index, case_node_pt
int high_bound = node_has_high_bound (node, index_type);
int low_bound = node_has_low_bound (node, index_type);
- if (!high_bound && low_bound)
+ if (default_label && !high_bound && low_bound)
{
probability = conditional_probability (
default_prob,
@@ -1984,7 +2013,7 @@ emit_case_nodes (rtx index, case_node_pt
probability);
}
- else if (!low_bound && high_bound)
+ else if (default_label && !low_bound && high_bound)
{
probability = conditional_probability (
default_prob,
@@ -1998,7 +2027,7 @@ emit_case_nodes (rtx index, case_node_pt
default_label,
probability);
}
- else if (!low_bound && !high_bound)
+ else if (default_label && !low_bound && !high_bound)
{
/* Widen LOW and HIGH to the same width as INDEX. */
tree type = lang_hooks.types.type_for_mode (mode, unsignedp);
Index: gcc/testsuite/gcc.target/powerpc/pr51513.c
===================================================================
--- gcc/testsuite/gcc.target/powerpc/pr51513.c (nonexistent)
+++ gcc/testsuite/gcc.target/powerpc/pr51513.c (working copy)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target { powerpc*-*-linux* } } } */
+/* { dg-options "-O2 -fjump-tables --param case-values-threshold=1" } */
+/* Verify we created a jump table. */
+/* { dg-final { scan-assembler-times "mtctr " 1 } } */
+/* { dg-final { scan-assembler-times "bctr" 1 } } */
+/* Verify we eliminated the range check. */
+/* { dg-final { scan-assembler-not "cmpldi" } } */
+/* { dg-final { scan-assembler-not "cmplwi" } } */
+
+long
+bug (long cond, long v0, long v1, long v2)
+{
+ switch (cond)
+ {
+ case 0:
+ return v0;
+ case 1:
+ return v1;
+ case 2:
+ return v2;
+ default:
+ __builtin_unreachable ();
+ }
+ __builtin_abort ();
+}
Index: gcc/tree-cfg.c
===================================================================
--- gcc/tree-cfg.c (revision 246661)
+++ gcc/tree-cfg.c (working copy)
@@ -452,6 +452,37 @@ computed_goto_p (gimple *t)
&& TREE_CODE (gimple_goto_dest (t)) != LABEL_DECL);
}
+/* Returns true if the basic block BB has no successors and only contains
+ a call to __builtin_unreachable (). */
+
+bool
+gimple_unreachable_bb_p (basic_block bb)
+{
+ gimple_stmt_iterator gsi;
+ gimple_seq stmts = bb_seq (bb);
+ gimple *stmt;
+
+ if (EDGE_COUNT (bb->succs) != 0)
+ return false;
+
+ gsi = gsi_start (stmts);
+ while (!gsi_end_p (gsi) && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
+ gsi_next (&gsi);
+
+ if (gsi_end_p (gsi))
+ return false;
+
+ stmt = gsi_stmt (gsi);
+ while (is_gimple_debug (stmt) || gimple_clobber_p (stmt))
+ {
+ gsi_next (&gsi);
+ if (gsi_end_p (gsi))
+ return false;
+ stmt = gsi_stmt (gsi);
+ }
+ return gimple_call_builtin_p (stmt, BUILT_IN_UNREACHABLE);
+}
+
/* Returns true for edge E where e->src ends with a GIMPLE_COND and
the other edge points to a bb with just __builtin_unreachable ().
I.e. return true for C->M edge in:
@@ -475,28 +506,11 @@ assert_unreachable_fallthru_edge_p (edge
basic_block other_bb = EDGE_SUCC (pred_bb, 0)->dest;
if (other_bb == e->dest)
other_bb = EDGE_SUCC (pred_bb, 1)->dest;
- if (EDGE_COUNT (other_bb->succs) == 0)
- {
- gimple_stmt_iterator gsi = gsi_after_labels (other_bb);
- gimple *stmt;
-
- if (gsi_end_p (gsi))
- return false;
- stmt = gsi_stmt (gsi);
- while (is_gimple_debug (stmt) || gimple_clobber_p (stmt))
- {
- gsi_next (&gsi);
- if (gsi_end_p (gsi))
- return false;
- stmt = gsi_stmt (gsi);
- }
- return gimple_call_builtin_p (stmt, BUILT_IN_UNREACHABLE);
- }
+ return gimple_unreachable_bb_p (other_bb);
}
return false;
}
-
/* Initialize GF_CALL_CTRL_ALTERING flag, which indicates the call
could alter control flow except via eh. We initialize the flag at
CFG build time and only ever clear it later. */
Index: gcc/tree-cfg.h
===================================================================
--- gcc/tree-cfg.h (revision 246661)
+++ gcc/tree-cfg.h (working copy)
@@ -56,6 +56,7 @@ extern bool is_ctrl_stmt (gimple *);
extern bool is_ctrl_altering_stmt (gimple *);
extern bool simple_goto_p (gimple *);
extern bool stmt_ends_bb_p (gimple *);
+extern bool gimple_unreachable_bb_p (basic_block);
extern bool assert_unreachable_fallthru_edge_p (edge);
extern void delete_tree_cfg_annotations (function *);
extern gphi *get_virtual_phi (basic_block);