https://gcc.gnu.org/g:4fe10ce56c7c0fb47adb789ece804463e09a5534

commit r17-1844-g4fe10ce56c7c0fb47adb789ece804463e09a5534
Author: Konstantinos Eleftheriou <[email protected]>
Date:   Thu Jun 18 09:48:04 2026 +0200

    uninit: Relax PHI def predicate by maybe-undef edge conditions
    
    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.

Diff:
---
 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 44ce0765b2d6..2acf1b5d7458 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 e2d2fd25d152..ce3006a20d15 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;

Reply via email to