When ifcombine has combined the conditions guarding a definition, the guard
that distinguishes the defined value from the undefined one can end up on the
maybe-undef incoming edge of the merge PHI rather than on the use's
control-dependence chain. The definition-predicate superset test in
uninit_analysis::is_use_guarded then fails on that conjunct and a bogus
-Wmaybe-uninitialized warning is emitted.
When the superset test fails, drop from a copy of the definition predicate any
conjunct implied by the incoming-edge condition of every maybe-undef operand,
and retry. Such a conjunct cannot make the use unsafe: when it is false the
maybe-undef edge is not taken either, so no undefined value reaches the PHI.
The edge condition is found by walking up single-predecessor forwarder blocks
to the controlling conditional.
gcc/ChangeLog:
* gimple-predicate-analysis.cc (get_pred_info_from_cond_edge): New
function.
(predicate::init_from_control_deps): Use it.
(predicate::drop_conjuncts_implied_by): New method.
(uninit_analysis::is_use_guarded): Retry the superset test after
relaxing the definition predicate by the maybe-undef edge conditions.
* gimple-predicate-analysis.h (class predicate): Declare
drop_conjuncts_implied_by.
Signed-off-by: Konstantinos Eleftheriou <[email protected]>
---
(no changes since v1)
gcc/gimple-predicate-analysis.cc | 96 ++++++++++++++++++++++++++++++--
gcc/gimple-predicate-analysis.h | 2 +
2 files changed, 93 insertions(+), 5 deletions(-)
diff --git a/gcc/gimple-predicate-analysis.cc b/gcc/gimple-predicate-analysis.cc
index 3df0c11070a7..addae1908e11 100644
--- a/gcc/gimple-predicate-analysis.cc
+++ b/gcc/gimple-predicate-analysis.cc
@@ -826,6 +826,42 @@ predicate::superset_of (const predicate &preds) const
return true;
}
+/* Remove from every chain in *THIS any (comparison) conjunct whose domain is
+ a superset of every predicate in EDGE_CONDS, i.e. a conjunct implied by all
+ of them. Returns true if any conjunct was removed. */
+
+bool
+predicate::drop_conjuncts_implied_by (const vec<pred_info> &edge_conds)
+{
+ if (edge_conds.is_empty ())
+ return false;
+
+ bool changed = false;
+ for (unsigned i = 0; i < m_preds.length (); i++)
+ {
+ pred_chain &chain = m_preds[i];
+ for (unsigned j = 0; j < chain.length (); )
+ {
+ /* Only reason about comparison conjuncts: subset_of negates the
+ code for inverted predicates, which is invalid for e.g. the
+ BIT_AND_EXPR of an "x & mask" predicate. */
+ bool implied = TREE_CODE_CLASS (chain[j].cond_code) == tcc_comparison;
+ for (unsigned k = 0; implied && k < edge_conds.length (); k++)
+ if (!subset_of (edge_conds[k], chain[j]))
+ implied = false;
+
+ if (implied)
+ {
+ chain.ordered_remove (j);
+ changed = true;
+ }
+ else
+ j++;
+ }
+ }
+ return changed;
+}
+
/* Create a predicate of the form OP != 0 and push it the work list CHAIN. */
static void
@@ -857,6 +893,22 @@ get_pred_info_from_cmp (const gimple *cmp_assign)
return pred;
}
+/* Return a pred_info for the GIMPLE_COND ending E's source block. The true
+ edge corresponds to the condition holding, so a false edge yields the
+ inverted predicate. */
+
+static pred_info
+get_pred_info_from_cond_edge (edge e)
+{
+ gcond *cond = as_a<gcond *> (*gsi_last_bb (e->src));
+ pred_info pred;
+ pred.pred_lhs = gimple_cond_lhs (cond);
+ pred.pred_rhs = gimple_cond_rhs (cond);
+ pred.cond_code = gimple_cond_code (cond);
+ pred.invert = !!(e->flags & EDGE_FALSE_VALUE);
+ return pred;
+}
+
/* If PHI is a degenerate phi with all operands having the same value (relop)
update *PRED to that value and return true. Otherwise return false. */
@@ -1826,11 +1878,7 @@ predicate::init_from_control_deps (const vec<edge>
*dep_chains,
/* The true edge corresponds to the uninteresting condition.
Add the negated predicate(s) for the edge to record
the interesting condition. */
- pred_info one_pred;
- one_pred.pred_lhs = gimple_cond_lhs (cond_stmt);
- one_pred.pred_rhs = gimple_cond_rhs (cond_stmt);
- one_pred.cond_code = gimple_cond_code (cond_stmt);
- one_pred.invert = !!(e->flags & EDGE_FALSE_VALUE);
+ pred_info one_pred = get_pred_info_from_cond_edge (e);
t_chain.safe_push (one_pred);
@@ -2242,6 +2290,44 @@ uninit_analysis::is_use_guarded (gimple *use_stmt,
basic_block use_bb,
if (m_phi_def_preds.superset_of (use_preds))
return true;
+ /* The superset test fails when a guard that distinguishes the defined from
+ the undefined value sits on the PHI's maybe-undef incoming edge rather
+ than on the use's control-dependence chain -- e.g. after ifcombine merges
+ the conditions guarding the definition. A conjunct of the definition
+ predicate that is implied by the incoming-edge condition of every
+ maybe-undef operand cannot make the use unsafe: when that conjunct is
+ false the maybe-undef edge is not taken either, so no undefined value
+ reaches the PHI. Drop such conjuncts from a copy of the definition
+ predicate and retry; bail unless every maybe-undef operand arrives on a
+ simple conditional edge. */
+ auto_vec<pred_info, 4> edge_conds;
+ unsigned nargs = gimple_phi_num_args (phi);
+ for (unsigned i = 0; i < nargs && i < m_eval.max_phi_args; i++)
+ {
+ if (!MASK_TEST_BIT (opnds, i))
+ continue;
+ edge e = gimple_phi_arg_edge (phi, i);
+ /* Walk up through forwarder blocks to the controlling conditional; a
+ single-predecessor forwarder preserves the implication. */
+ while (single_pred_p (e->src) && single_succ_p (e->src))
+ e = single_pred_edge (e->src);
+ if (EDGE_COUNT (e->src->succs) != 2
+ || !safe_dyn_cast <gcond *> (*gsi_last_bb (e->src)))
+ {
+ edge_conds.truncate (0);
+ break;
+ }
+ edge_conds.safe_push (get_pred_info_from_cond_edge (e));
+ }
+
+ if (!edge_conds.is_empty ())
+ {
+ predicate relaxed (m_phi_def_preds);
+ if (relaxed.drop_conjuncts_implied_by (edge_conds)
+ && relaxed.superset_of (use_preds))
+ return true;
+ }
+
return false;
}
diff --git a/gcc/gimple-predicate-analysis.h b/gcc/gimple-predicate-analysis.h
index 218b34ffddab..7bc6e73506c8 100644
--- a/gcc/gimple-predicate-analysis.h
+++ b/gcc/gimple-predicate-analysis.h
@@ -91,6 +91,8 @@ class predicate
bool superset_of (const predicate &) const;
+ bool drop_conjuncts_implied_by (const vec<pred_info> &);
+
private:
bool includes (const pred_chain &) const;
--
2.52.0