On Fri, Nov 13, 2026 at 8:45 AM Sendao Yan <[email protected]> wrote:
>
> Moving pass_lower_switch after the late jump threading changed the
> control flow seen by the uninitialized warning pass, exposing two
> false positives (PR107919 and PR44547).  Both stem from the same
> defect in uninit_analysis::is_use_guarded.
>
> is_use_guarded treats an empty use predicate (the PHI is defined and
> used in the same block) as an unguarded use because init_use_preds
> returns false when the predicate is empty.  That is wrong: whether
> such a use is guarded depends solely on the definition predicate, so
> fall through and compute it.
>
> An empty (TRUE) use predicate is then matched by m_phi_def_preds being
> TRUE, i.e. the value is defined on every path.  Several spots made this
> mismatch on a TRUE use predicate:
>
>   - predicate::simplify_2 folded a tautological disjunction to TRUE by
>     clearing m_preds but forgot to set m_cval, so a definition
>     predicate that simplified to TRUE was misread as FALSE ("never
>     defined").  Set m_cval as well.
>
>   - the fallback path only recognized an implied guard when the
>     relaxed definition predicate was a superset of the use predicate,
>     which is vacuous for a TRUE use predicate; simplify and normalize
>     the relaxed predicate and, if it is TRUE (defined on every path),
>     treat it as guarding any use, otherwise fall back to superset_of
>     for a conditional use.
>
>   - normalize() represents an "x & mask" conjunct as a BIT_AND_EXPR
>     pred_info, but simplify_1b() and pred_neg_p() only handle
>     comparisons and pass the code to invert_tree_comparison() and
>     maybe_fold_and_comparisons(), which both trap on anything else.
>     Skip non-comparison conjuncts in simplify_1b() and let pred_neg_p()
>     answer "not a negation" for them rather than comparing their bare
>     codes.  This makes simplifying an already normalized predicate
>     safe, as the fallback above now does.
>
>   - drop_conjuncts_implied_by() can remove every conjunct of a chain;
>     the resulting TRUE chain is neither reported as TRUE nor removed,
>     leaving an empty chain in the predicate.  Like simplify_2(), turn
>     that into an empty predicate with m_cval set.

Can you address the issues reported by Sashiko?  The overall
changes look OK to me.

Thanks,
Richard.

> gcc/
>         * gimple-predicate-analysis.cc (pred_neg_p): Return false for
>         non-comparison operands instead of inverting their code.
>         (predicate::drop_conjuncts_implied_by): Simplify the whole
>         predicate to TRUE when it empties a chain.
>         (simplify_1b): Skip non-comparison pred pairs.
>         (predicate::simplify_2): Set m_cval when a disjunction simplifies
>         to true.
>         (uninit_analysis::is_use_guarded): Do not return early for an
>         empty use predicate; skip the overlap pruning and the superset
>         test for an unconditional use, which superset_of() cannot prove;
>         simplify and normalize the relaxed definition predicate in the
>         fallback and treat a TRUE relaxed predicate as guarding any use.
> ---
>  gcc/gimple-predicate-analysis.cc | 78 +++++++++++++++++++++++++-------
>  1 file changed, 62 insertions(+), 16 deletions(-)
>
> diff --git a/gcc/gimple-predicate-analysis.cc 
> b/gcc/gimple-predicate-analysis.cc
> index 384fe32078e..0b6c3f64efc 100644
> --- a/gcc/gimple-predicate-analysis.cc
> +++ b/gcc/gimple-predicate-analysis.cc
> @@ -62,11 +62,17 @@ pred_neg_p (const pred_info &x1, const pred_info &x2)
>        || !operand_equal_p (x1.pred_rhs, x2.pred_rhs, 0))
>      return false;
>
> -  tree_code c1 = x1.cond_code, c2;
> +  tree_code c1 = x1.cond_code, c2 = x2.cond_code;
>    if (x1.invert == x2.invert)
> -    c2 = invert_tree_comparison (x2.cond_code, false);
> -  else
> -    c2 = x2.cond_code;
> +    {
> +      /* Only comparisons have a complement; an "x & mask" conjunct is a
> +        BIT_AND_EXPR pred_info (see normalize()) and is never the negation
> +        of anything, while invert_tree_comparison() would trap on it.  */
> +      if (TREE_CODE_CLASS (c1) != tcc_comparison
> +         || TREE_CODE_CLASS (c2) != tcc_comparison)
> +       return false;
> +      c2 = invert_tree_comparison (c2, false);
> +    }
>
>    return c1 == c2;
>  }
> @@ -828,7 +834,9 @@ predicate::superset_of (const predicate &preds) const
>
>  /* 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.  */
> +   of them.  Returns true if any conjunct was removed.  If all the conjuncts
> +   of a chain are removed the chain is TRUE, and with it the whole predicate,
> +   which is then left empty with M_CVAL set.  */
>
>  bool
>  predicate::drop_conjuncts_implied_by (const vec<pred_info> &edge_conds)
> @@ -858,6 +866,18 @@ predicate::drop_conjuncts_implied_by (const 
> vec<pred_info> &edge_conds)
>           else
>             j++;
>         }
> +
> +      /* An empty conjunct is TRUE, and so is the OR of the chains.  Making
> +        that explicit keeps is_true() in sync with superset_of() and spares
> +        the callers from having to deal with empty chains.  */
> +      if (chain.is_empty ())
> +       {
> +         for (unsigned k = 0; k < m_preds.length (); k++)
> +           m_preds[k].release ();
> +         m_preds.truncate (0);
> +         m_cval = true;
> +         return true;
> +       }
>      }
>    return changed;
>  }
> @@ -1310,6 +1330,14 @@ simplify_1b (pred_chain &chain)
>                        && CONSTANT_CLASS_P (b_pred.pred_rhs))))
>             continue;
>
> +         /* Only comparison conjuncts can be combined.  An "x & mask"
> +            conjunct is a BIT_AND_EXPR pred_info (see normalize()), and
> +            inverting it or feeding it to maybe_fold_and_comparisons()
> +            would run into invert_tree_comparison()'s gcc_unreachable().  */
> +         if (TREE_CODE_CLASS (a_pred.cond_code) != tcc_comparison
> +             || TREE_CODE_CLASS (b_pred.cond_code) != tcc_comparison)
> +           continue;
> +
>           tree_code a_code = a_pred.cond_code;
>           if (a_pred.invert)
>             a_code = invert_tree_comparison (a_code, false);
> @@ -1403,6 +1431,8 @@ predicate::simplify_2 ()
>                   for (unsigned k = 0; k < m_preds.length (); ++k)
>                     m_preds[k].release ();
>                   m_preds.truncate (0);
> +                 /* The whole disjunction has simplified to true.  */
> +                 m_cval = true;
>                 }
>               break;
>             }
> @@ -2258,18 +2288,22 @@ uninit_analysis::is_use_guarded (gimple *use_stmt, 
> basic_block use_bb,
>       into its use.  This will be empty if the PHI is defined and used
>       in the same bb.  */
>    predicate use_preds (true);
> -  if (!init_use_preds (use_preds, def_bb, use_bb))
> -    return false;
> +  init_use_preds (use_preds, def_bb, use_bb);
> +  /* init_use_preds() returns false for an empty (unconditional) use
> +     predicate.  That does not make the use unguarded: whether it is
> +     guarded then depends solely on the definition predicate, which is
> +     computed below.  */
>
>    use_preds.simplify (use_stmt, /*is_use=*/true);
>    use_preds.normalize (use_stmt, /*is_use=*/true);
>    if (use_preds.is_false ())
>      return true;
> -  if (use_preds.is_true ())
> -    return false;
>
> -  /* Try to prune the dead incoming phi edges.  */
> -  if (!overlap (phi, opnds, visited, use_preds))
> +  /* Try to prune the dead incoming phi edges.  This requires a non-trivial
> +     use predicate; an unconditional use (USE_PREDS is TRUE) has nothing to
> +     check against.  */
> +  if (!use_preds.is_true ()
> +      && !overlap (phi, opnds, visited, use_preds))
>      {
>        if (DEBUG_PREDICATE_ANALYZER && dump_file)
>         fputs ("found predicate overlap\n", dump_file);
> @@ -2293,8 +2327,11 @@ uninit_analysis::is_use_guarded (gimple *use_stmt, 
> basic_block use_bb,
>
>    /* Return true if the predicate guarding the valid definition (i.e.,
>       *THIS) is a superset of the predicate guarding the use (i.e.,
> -     USE_PREDS).  */
> -  if (m_phi_def_preds.superset_of (use_preds))
> +     USE_PREDS).  An unconditional use (USE_PREDS is TRUE) is guarded only
> +     if the value is defined on every path; that case was handled above (an
> +     empty definition predicate returns early), and superset_of() is vacuous
> +     for a TRUE use predicate, so there is nothing left to test here.  */
> +  if (!use_preds.is_true () && m_phi_def_preds.superset_of (use_preds))
>      return true;
>
>    /* The superset test fails when a guard that distinguishes the defined from
> @@ -2330,9 +2367,18 @@ uninit_analysis::is_use_guarded (gimple *use_stmt, 
> basic_block use_bb,
>    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;
> +      if (relaxed.drop_conjuncts_implied_by (edge_conds))
> +       {
> +         relaxed.simplify (phi);
> +         relaxed.normalize (phi);
> +         /* A definition predicate that simplifies to TRUE guards any use;
> +            conversely an unconditional use (USE_PREDS is TRUE) is guarded
> +            only by a TRUE definition predicate, for which superset_of()
> +            is vacuous.  */
> +         if (relaxed.is_true ()
> +             || (!use_preds.is_true () && relaxed.superset_of (use_preds)))
> +           return true;
> +       }
>      }
>
>    return false;
> --
> 2.34.1
>

Reply via email to