gimple *);
static int value_replacement (basic_block, basic_block,
edge, edge, gphi *, tree, tree);
-static bool minmax_replacement (basic_block, basic_block,
- edge, edge, gphi *, tree, tree);
+static bool minmax_replacement (basic_block, basic_block, basic_block,
+ edge, edge, gphi *, tree, tree, bool);
static bool spaceship_replacement (basic_block, basic_block,
edge, edge, gphi *, tree, tree);
static bool cond_removal_in_builtin_zero_pattern (basic_block, basic_block,
basic_block bb1, bb2;
edge e1, e2;
tree arg0, arg1;
+ bool diamond_p = false;
bb = bb_order[i];
hoist_adjacent_loads (bb, bb1, bb2, bb3);
continue;
}
+ else if (EDGE_SUCC (bb1, 0)->dest == EDGE_SUCC (bb2, 0)->dest
+ && !empty_block_p (bb1))
+ diamond_p = true;
else
continue;
}
else
{
- gimple_seq phis = phi_nodes (bb2);
gimple_stmt_iterator gsi;
bool candorest = true;
+ /* Check that we're looking for nested phis. */
+ basic_block merge = diamond_p ? EDGE_SUCC (bb2, 0)->dest : bb2;
+ gimple_seq phis = phi_nodes (merge);
+
/* Value replacement can work with more than one PHI
so try that first. */
if (!early_p)
if (!candorest)
continue;
+ e2 = diamond_p ? EDGE_SUCC (bb2, 0) : e2;
phi = single_non_singleton_phi_for_edges (phis, e1, e2);
if (!phi)
continue;
gphi *newphi;
if (single_pred_p (bb1)
+ && !diamond_p
&& (newphi = factor_out_conditional_conversion (e1, e2, phi,
arg0, arg1,
cond_stmt)))
}
/* Do the replacement of conditional if it can be done. */
- if (!early_p && two_value_replacement (bb, bb1, e2, phi, arg0, arg1))
+ if (!early_p
+ && !diamond_p
+ && two_value_replacement (bb, bb1, e2, phi, arg0, arg1))
cfgchanged = true;
- else if (match_simplify_replacement (bb, bb1, e1, e2, phi,
- arg0, arg1,
- early_p))
+ else if (!diamond_p
+ && match_simplify_replacement (bb, bb1, e1, e2, phi,
+ arg0, arg1, early_p))
cfgchanged = true;
else if (!early_p
+ && !diamond_p
&& single_pred_p (bb1)
&& cond_removal_in_builtin_zero_pattern (bb, bb1, e1, e2,
phi, arg0, arg1))
cfgchanged = true;
- else if (minmax_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
+ else if (minmax_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1,
+ diamond_p))
cfgchanged = true;
else if (single_pred_p (bb1)
+ && !diamond_p
&& spaceship_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
cfgchanged = true;
}
SET_USE (PHI_ARG_DEF_PTR (phi, e->dest_idx), new_tree);
/* Remove the empty basic block. */
- edge edge_to_remove;
+ edge edge_to_remove = NULL, keep_edge = NULL;
if (EDGE_SUCC (cond_block, 0)->dest == bb)
- edge_to_remove = EDGE_SUCC (cond_block, 1);
+ {
+ edge_to_remove = EDGE_SUCC (cond_block, 1);
+ keep_edge = EDGE_SUCC (cond_block, 0);
+ }
+ else if (EDGE_SUCC (cond_block, 1)->dest == bb)
+ {
+ edge_to_remove = EDGE_SUCC (cond_block, 0);
+ keep_edge = EDGE_SUCC (cond_block, 1);
+ }
+ else if ((keep_edge = find_edge (cond_block, e->src)))
+ ;
else
- edge_to_remove = EDGE_SUCC (cond_block, 0);
- if (EDGE_COUNT (edge_to_remove->dest->preds) == 1)
+ gcc_unreachable ();
+
+ if (edge_to_remove && EDGE_COUNT (edge_to_remove->dest->preds) == 1)
{
e->flags |= EDGE_FALLTHRU;
e->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
CFG cleanup deal with the edge removal to avoid
updating dominators here in a non-trivial way. */
gcond *cond = as_a <gcond *> (last_stmt (cond_block));
- if (edge_to_remove->flags & EDGE_TRUE_VALUE)
+ if (keep_edge->flags & EDGE_FALSE_VALUE)
gimple_cond_make_false (cond);
- else
+ else if (keep_edge->flags & EDGE_TRUE_VALUE)
gimple_cond_make_true (cond);
}
return 0;
}
+/* If VAR is an SSA_NAME that points to a BIT_NOT_EXPR then return the TREE for
+ the value being inverted. */
+
+static tree
+strip_bit_not (tree var)
+{
+ if (TREE_CODE (var) != SSA_NAME)
+ return NULL_TREE;
+
+ gimple *assign = SSA_NAME_DEF_STMT (var);
+ if (gimple_code (assign) != GIMPLE_ASSIGN)
+ return NULL_TREE;
+
+ if (gimple_assign_rhs_code (assign) != BIT_NOT_EXPR)
+ return NULL_TREE;
+
+ return gimple_assign_rhs1 (assign);
+}
+
+/* Invert a MIN to a MAX or a MAX to a MIN expression CODE. */
+
+enum tree_code
+invert_minmax_code (enum tree_code code)
+{
+ switch (code) {
+ case MIN_EXPR:
+ return MAX_EXPR;
+ case MAX_EXPR:
+ return MIN_EXPR;
+ default:
+ gcc_unreachable ();
+ }
+}
+
/* The function minmax_replacement does the main work of doing the minmax
replacement. Return true if the replacement is done. Otherwise return
false.
BB is the basic block where the replacement is going to be done on. ARG0
- is argument 0 from the PHI. Likewise for ARG1. */
+ is argument 0 from the PHI. Likewise for ARG1.
+
+ If THREEWAY_P then expect the BB to be laid out in diamond shape with each
+ BB containing only a MIN or MAX expression. */
static bool
-minmax_replacement (basic_block cond_bb, basic_block middle_bb,
- edge e0, edge e1, gphi *phi, tree arg0, tree arg1)
+minmax_replacement (basic_block cond_bb, basic_block middle_bb, basic_block alt_middle_bb,
+ edge e0, edge e1, gphi *phi, tree arg0, tree arg1, bool threeway_p)
{
tree result;
edge true_edge, false_edge;
if (false_edge->dest == middle_bb)
false_edge = EDGE_SUCC (false_edge->dest, 0);
+ /* When THREEWAY_P then e1 will point to the edge of the final transition
+ from middle-bb to end. */
if (true_edge == e0)
{
- gcc_assert (false_edge == e1);
+ if (!threeway_p)
+ gcc_assert (false_edge == e1);
arg_true = arg0;
arg_false = arg1;
}
else
{
gcc_assert (false_edge == e0);
- gcc_assert (true_edge == e1);
+ if (!threeway_p)
+ gcc_assert (true_edge == e1);
arg_true = arg1;
arg_false = arg0;
}
else
return false;
}
+ else if (middle_bb != alt_middle_bb && threeway_p)
+ {
+ /* Recognize the following case:
+
+ if (smaller < larger)
+ a = MIN (smaller, c);
+ else
+ b = MIN (larger, c);
+ x = PHI <a, b>
+
+ This is equivalent to
+
+ a = MIN (smaller, c);
+ x = MIN (larger, a); */
+
+ gimple *assign = last_and_only_stmt (middle_bb);
+ tree lhs, op0, op1, bound;
+ tree alt_lhs, alt_op0, alt_op1;
+ bool invert = false;
+
+ if (!single_pred_p (middle_bb)
+ || !single_pred_p (alt_middle_bb)
+ || !single_succ_p (middle_bb)
+ || !single_succ_p (alt_middle_bb))
+ return false;
+
+ /* When THREEWAY_P then e1 will point to the edge of the final transition
+ from middle-bb to end. */
+ if (true_edge == e0)
+ gcc_assert (false_edge == EDGE_PRED (e1->src, 0));
+ else
+ gcc_assert (true_edge == EDGE_PRED (e1->src, 0));
+
+ bool valid_minmax_p = false;
+ gimple_stmt_iterator it1
+ = gsi_start_nondebug_after_labels_bb (middle_bb);
+ gimple_stmt_iterator it2
+ = gsi_start_nondebug_after_labels_bb (alt_middle_bb);
+ if (gsi_one_nondebug_before_end_p (it1)
+ && gsi_one_nondebug_before_end_p (it2))
+ {
+ gimple *stmt1 = gsi_stmt (it1);
+ gimple *stmt2 = gsi_stmt (it2);
+ if (is_gimple_assign (stmt1) && is_gimple_assign (stmt2))
+ {
+ enum tree_code code1 = gimple_assign_rhs_code (stmt1);
+ enum tree_code code2 = gimple_assign_rhs_code (stmt2);
+ valid_minmax_p = (code1 == MIN_EXPR || code1 == MAX_EXPR)
+ && (code2 == MIN_EXPR || code2 == MAX_EXPR);
+ }
+ }
+
+ if (!valid_minmax_p)
+ return false;
+
+ if (!assign
+ || gimple_code (assign) != GIMPLE_ASSIGN)
+ return false;
+
+ lhs = gimple_assign_lhs (assign);
+ ass_code = gimple_assign_rhs_code (assign);
+ if (ass_code != MAX_EXPR && ass_code != MIN_EXPR)
+ return false;
+
+ op0 = gimple_assign_rhs1 (assign);
+ op1 = gimple_assign_rhs2 (assign);
+
+ assign = last_and_only_stmt (alt_middle_bb);
+ if (!assign
+ || gimple_code (assign) != GIMPLE_ASSIGN)
+ return false;
+
+ alt_lhs = gimple_assign_lhs (assign);
+ if (ass_code != gimple_assign_rhs_code (assign))
+ return false;
+
+ if (!operand_equal_for_phi_arg_p (lhs, arg_true)
+ || !operand_equal_for_phi_arg_p (alt_lhs, arg_false))
+ return false;
+
+ alt_op0 = gimple_assign_rhs1 (assign);
+ alt_op1 = gimple_assign_rhs2 (assign);
+
+ if ((operand_equal_for_phi_arg_p (op0, smaller)
+ || (alt_smaller
+ && operand_equal_for_phi_arg_p (op0, alt_smaller)))
+ && (operand_equal_for_phi_arg_p (alt_op0, larger)
+ || (alt_larger
+ && operand_equal_for_phi_arg_p (alt_op0, alt_larger))))
+ {
+ /* We got here if the condition is true, i.e., SMALLER < LARGER. */
+ if (!operand_equal_for_phi_arg_p (op1, alt_op1))
+ return false;
+
+ if ((arg0 = strip_bit_not (op0)) != NULL
+ && (arg1 = strip_bit_not (alt_op0)) != NULL
+ && (bound = strip_bit_not (op1)) != NULL)
+ {
+ minmax = MAX_EXPR;
+ ass_code = invert_minmax_code (ass_code);
+ invert = true;
+ }
+ else
+ {
+ bound = op1;
+ minmax = MIN_EXPR;
+ arg0 = op0;
+ arg1 = alt_op0;
+ }
+ }
+ else if ((operand_equal_for_phi_arg_p (op0, larger)
+ || (alt_larger
+ && operand_equal_for_phi_arg_p (op0, alt_larger)))
+ && (operand_equal_for_phi_arg_p (alt_op0, smaller)
+ || (alt_smaller
+ && operand_equal_for_phi_arg_p (alt_op0, alt_smaller))))
+ {
+ /* We got here if the condition is true, i.e., SMALLER > LARGER. */
+ if (!operand_equal_for_phi_arg_p (op1, alt_op1))
+ return false;
+
+ if ((arg0 = strip_bit_not (op0)) != NULL
+ && (arg1 = strip_bit_not (alt_op0)) != NULL
+ && (bound = strip_bit_not (op1)) != NULL)
+ {
+ minmax = MIN_EXPR;
+ ass_code = invert_minmax_code (ass_code);
+ invert = true;
+ }
+ else
+ {
+ bound = op1;
+ minmax = MAX_EXPR;
+ arg0 = op0;
+ arg1 = alt_op0;
+ }
+ }
+ else
+ return false;
+
+ /* Emit the statement to compute min/max. */
+ location_t locus = gimple_location (last_stmt (cond_bb));
+ gimple_seq stmts = NULL;
+ tree phi_result = PHI_RESULT (phi);
+ result = gimple_build (&stmts, locus, minmax, TREE_TYPE (phi_result),
+ arg0, bound);
+ result = gimple_build (&stmts, locus, ass_code, TREE_TYPE (phi_result),
+ result, arg1);
+ if (invert)
+ result = gimple_build (&stmts, locus, BIT_NOT_EXPR, TREE_TYPE (phi_result),
+ result);
+
+ gsi = gsi_last_bb (cond_bb);
+ gsi_insert_seq_before (&gsi, stmts, GSI_NEW_STMT);
+
+ replace_phi_edge_with_variable (cond_bb, e1, phi, result);
+
+ return true;
+ }
else
{
/* Recognize the following case, assuming d <= u: