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
>