blob: bc9ed847267dcb58eb5d2b4956ae6665df82a6f9 [file] [log] [blame]
/* Support for simple predicate analysis.
Copyright (C) 2001-2022 Free Software Foundation, Inc.
Contributed by Xinliang David Li <davidxl@google.com>
Generalized by Martin Sebor <msebor@redhat.com>
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#define INCLUDE_STRING
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "tree.h"
#include "gimple.h"
#include "tree-pass.h"
#include "ssa.h"
#include "gimple-pretty-print.h"
#include "diagnostic-core.h"
#include "fold-const.h"
#include "gimple-iterator.h"
#include "tree-ssa.h"
#include "tree-cfg.h"
#include "cfghooks.h"
#include "attribs.h"
#include "builtins.h"
#include "calls.h"
#include "value-query.h"
#include "cfganal.h"
#include "tree-eh.h"
#include "gimple-predicate-analysis.h"
#define DEBUG_PREDICATE_ANALYZER 1
/* In our predicate normal form we have MAX_NUM_CHAINS or predicates
and in those MAX_CHAIN_LEN (inverted) and predicates. */
#define MAX_NUM_CHAINS 8
#define MAX_CHAIN_LEN 5
/* Return true if X1 is the negation of X2. */
static inline bool
pred_neg_p (const pred_info &x1, const pred_info &x2)
{
if (!operand_equal_p (x1.pred_lhs, x2.pred_lhs, 0)
|| !operand_equal_p (x1.pred_rhs, x2.pred_rhs, 0))
return false;
tree_code c1 = x1.cond_code, c2;
if (x1.invert == x2.invert)
c2 = invert_tree_comparison (x2.cond_code, false);
else
c2 = x2.cond_code;
return c1 == c2;
}
/* Return whether the condition (VAL CMPC BOUNDARY) is true. */
static bool
is_value_included_in (tree val, tree boundary, tree_code cmpc)
{
/* Only handle integer constant here. */
if (TREE_CODE (val) != INTEGER_CST || TREE_CODE (boundary) != INTEGER_CST)
return true;
bool inverted = false;
if (cmpc == GE_EXPR || cmpc == GT_EXPR || cmpc == NE_EXPR)
{
cmpc = invert_tree_comparison (cmpc, false);
inverted = true;
}
bool result;
if (cmpc == EQ_EXPR)
result = tree_int_cst_equal (val, boundary);
else if (cmpc == LT_EXPR)
result = tree_int_cst_lt (val, boundary);
else
{
gcc_assert (cmpc == LE_EXPR);
result = tree_int_cst_le (val, boundary);
}
if (inverted)
result ^= 1;
return result;
}
/* Format the vector of edges EV as a string. */
static std::string
format_edge_vec (const vec<edge> &ev)
{
std::string str;
unsigned n = ev.length ();
for (unsigned i = 0; i < n; ++i)
{
char es[32];
const_edge e = ev[i];
sprintf (es, "%u -> %u", e->src->index, e->dest->index);
str += es;
if (i + 1 < n)
str += ", ";
}
return str;
}
/* Format the first N elements of the array of vector of edges EVA as
a string. */
static std::string
format_edge_vecs (const vec<edge> eva[], unsigned n)
{
std::string str;
for (unsigned i = 0; i != n; ++i)
{
str += '{';
str += format_edge_vec (eva[i]);
str += '}';
if (i + 1 < n)
str += ", ";
}
return str;
}
/* Dump a single pred_info to F. */
static void
dump_pred_info (FILE *f, const pred_info &pred)
{
if (pred.invert)
fprintf (f, "NOT (");
print_generic_expr (f, pred.pred_lhs);
fprintf (f, " %s ", op_symbol_code (pred.cond_code));
print_generic_expr (f, pred.pred_rhs);
if (pred.invert)
fputc (')', f);
}
/* Dump a pred_chain to F. */
static void
dump_pred_chain (FILE *f, const pred_chain &chain)
{
unsigned np = chain.length ();
for (unsigned j = 0; j < np; j++)
{
if (j > 0)
fprintf (f, " AND (");
else
fputc ('(', f);
dump_pred_info (f, chain[j]);
fputc (')', f);
}
}
/* Return the 'normalized' conditional code with operand swapping
and condition inversion controlled by SWAP_COND and INVERT. */
static tree_code
get_cmp_code (tree_code orig_cmp_code, bool swap_cond, bool invert)
{
tree_code tc = orig_cmp_code;
if (swap_cond)
tc = swap_tree_comparison (orig_cmp_code);
if (invert)
tc = invert_tree_comparison (tc, false);
switch (tc)
{
case LT_EXPR:
case LE_EXPR:
case GT_EXPR:
case GE_EXPR:
case EQ_EXPR:
case NE_EXPR:
break;
default:
return ERROR_MARK;
}
return tc;
}
/* Return true if PRED is common among all predicate chains in PREDS
(and therefore can be factored out). */
static bool
find_matching_predicate_in_rest_chains (const pred_info &pred,
const pred_chain_union &preds)
{
/* Trival case. */
if (preds.length () == 1)
return true;
for (unsigned i = 1; i < preds.length (); i++)
{
bool found = false;
const pred_chain &chain = preds[i];
unsigned n = chain.length ();
for (unsigned j = 0; j < n; j++)
{
const pred_info &pred2 = chain[j];
/* Can relax the condition comparison to not use address
comparison. However, the most common case is that
multiple control dependent paths share a common path
prefix, so address comparison should be ok. */
if (operand_equal_p (pred2.pred_lhs, pred.pred_lhs, 0)
&& operand_equal_p (pred2.pred_rhs, pred.pred_rhs, 0)
&& pred2.invert == pred.invert)
{
found = true;
break;
}
}
if (!found)
return false;
}
return true;
}
/* Find a predicate to examine against paths of interest. If there
is no predicate of the "FLAG_VAR CMP CONST" form, try to find one
of that's the form "FLAG_VAR CMP FLAG_VAR" with value range info.
PHI is the phi node whose incoming (interesting) paths need to be
examined. On success, return the comparison code, set defintion
gimple of FLAG_DEF and BOUNDARY_CST. Otherwise return ERROR_MARK. */
static tree_code
find_var_cmp_const (pred_chain_union preds, gphi *phi, gimple **flag_def,
tree *boundary_cst)
{
tree_code vrinfo_code = ERROR_MARK;
gimple *vrinfo_def = NULL;
tree vrinfo_cst = NULL;
gcc_assert (preds.length () > 0);
pred_chain chain = preds[0];
for (unsigned i = 0; i < chain.length (); i++)
{
bool use_vrinfo_p = false;
const pred_info &pred = chain[i];
tree cond_lhs = pred.pred_lhs;
tree cond_rhs = pred.pred_rhs;
if (cond_lhs == NULL_TREE || cond_rhs == NULL_TREE)
continue;
tree_code code = get_cmp_code (pred.cond_code, false, pred.invert);
if (code == ERROR_MARK)
continue;
/* Convert to the canonical form SSA_NAME CMP CONSTANT. */
if (TREE_CODE (cond_lhs) == SSA_NAME
&& is_gimple_constant (cond_rhs))
;
else if (TREE_CODE (cond_rhs) == SSA_NAME
&& is_gimple_constant (cond_lhs))
{
std::swap (cond_lhs, cond_rhs);
if ((code = get_cmp_code (code, true, false)) == ERROR_MARK)
continue;
}
/* Check if we can take advantage of FLAG_VAR COMP FLAG_VAR predicate
with value range info. Note only first of such case is handled. */
else if (vrinfo_code == ERROR_MARK
&& TREE_CODE (cond_lhs) == SSA_NAME
&& TREE_CODE (cond_rhs) == SSA_NAME)
{
gimple* lhs_def = SSA_NAME_DEF_STMT (cond_lhs);
if (!lhs_def || gimple_code (lhs_def) != GIMPLE_PHI
|| gimple_bb (lhs_def) != gimple_bb (phi))
{
std::swap (cond_lhs, cond_rhs);
if ((code = get_cmp_code (code, true, false)) == ERROR_MARK)
continue;
}
/* Check value range info of rhs, do following transforms:
flag_var < [min, max] -> flag_var < max
flag_var > [min, max] -> flag_var > min
We can also transform LE_EXPR/GE_EXPR to LT_EXPR/GT_EXPR:
flag_var <= [min, max] -> flag_var < [min, max+1]
flag_var >= [min, max] -> flag_var > [min-1, max]
if no overflow/wrap. */
tree type = TREE_TYPE (cond_lhs);
value_range r;
if (!INTEGRAL_TYPE_P (type)
|| !get_range_query (cfun)->range_of_expr (r, cond_rhs)
|| r.kind () != VR_RANGE)
continue;
wide_int min = r.lower_bound ();
wide_int max = r.upper_bound ();
if (code == LE_EXPR
&& max != wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)))
{
code = LT_EXPR;
max = max + 1;
}
if (code == GE_EXPR
&& min != wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type)))
{
code = GT_EXPR;
min = min - 1;
}
if (code == LT_EXPR)
cond_rhs = wide_int_to_tree (type, max);
else if (code == GT_EXPR)
cond_rhs = wide_int_to_tree (type, min);
else
continue;
use_vrinfo_p = true;
}
else
continue;
if ((*flag_def = SSA_NAME_DEF_STMT (cond_lhs)) == NULL)
continue;
if (gimple_code (*flag_def) != GIMPLE_PHI
|| gimple_bb (*flag_def) != gimple_bb (phi)
|| !find_matching_predicate_in_rest_chains (pred, preds))
continue;
/* Return if any "flag_var comp const" predicate is found. */
if (!use_vrinfo_p)
{
*boundary_cst = cond_rhs;
return code;
}
/* Record if any "flag_var comp flag_var[vinfo]" predicate is found. */
else if (vrinfo_code == ERROR_MARK)
{
vrinfo_code = code;
vrinfo_def = *flag_def;
vrinfo_cst = cond_rhs;
}
}
/* Return the "flag_var cmp flag_var[vinfo]" predicate we found. */
if (vrinfo_code != ERROR_MARK)
{
*flag_def = vrinfo_def;
*boundary_cst = vrinfo_cst;
}
return vrinfo_code;
}
/* Return true if all interesting opnds are pruned, false otherwise.
PHI is the phi node with interesting operands, OPNDS is the bitmap
of the interesting operand positions, FLAG_DEF is the statement
defining the flag guarding the use of the PHI output, BOUNDARY_CST
is the const value used in the predicate associated with the flag,
CMP_CODE is the comparison code used in the predicate, VISITED_PHIS
is the pointer set of phis visited, and VISITED_FLAG_PHIS is
the pointer to the pointer set of flag definitions that are also
phis.
Example scenario:
BB1:
flag_1 = phi <0, 1> // (1)
var_1 = phi <undef, some_val>
BB2:
flag_2 = phi <0, flag_1, flag_1> // (2)
var_2 = phi <undef, var_1, var_1>
if (flag_2 == 1)
goto BB3;
BB3:
use of var_2 // (3)
Because some flag arg in (1) is not constant, if we do not look into
the flag phis recursively, it is conservatively treated as unknown and
var_1 is thought to flow into use at (3). Since var_1 is potentially
uninitialized a false warning will be emitted.
Checking recursively into (1), the compiler can find out that only
some_val (which is defined) can flow into (3) which is OK. */
bool
uninit_analysis::prune_phi_opnds (gphi *phi, unsigned opnds, gphi *flag_def,
tree boundary_cst, tree_code cmp_code,
hash_set<gphi *> *visited_phis,
bitmap *visited_flag_phis)
{
/* The Boolean predicate guarding the PHI definition. Initialized
lazily from PHI in the first call to is_use_guarded() and cached
for subsequent iterations. */
uninit_analysis def_preds (m_eval);
unsigned n = MIN (m_eval.max_phi_args, gimple_phi_num_args (flag_def));
for (unsigned i = 0; i < n; i++)
{
if (!MASK_TEST_BIT (opnds, i))
continue;
tree flag_arg = gimple_phi_arg_def (flag_def, i);
if (!is_gimple_constant (flag_arg))
{
if (TREE_CODE (flag_arg) != SSA_NAME)
return false;
gphi *flag_arg_def = dyn_cast<gphi *> (SSA_NAME_DEF_STMT (flag_arg));
if (!flag_arg_def)
return false;
tree phi_arg = gimple_phi_arg_def (phi, i);
if (TREE_CODE (phi_arg) != SSA_NAME)
return false;
gphi *phi_arg_def = dyn_cast<gphi *> (SSA_NAME_DEF_STMT (phi_arg));
if (!phi_arg_def)
return false;
if (gimple_bb (phi_arg_def) != gimple_bb (flag_arg_def))
return false;
if (!*visited_flag_phis)
*visited_flag_phis = BITMAP_ALLOC (NULL);
tree phi_result = gimple_phi_result (flag_arg_def);
if (bitmap_bit_p (*visited_flag_phis, SSA_NAME_VERSION (phi_result)))
return false;
bitmap_set_bit (*visited_flag_phis, SSA_NAME_VERSION (phi_result));
/* Now recursively try to prune the interesting phi args. */
unsigned opnds_arg_phi = m_eval.phi_arg_set (phi_arg_def);
if (!prune_phi_opnds (phi_arg_def, opnds_arg_phi, flag_arg_def,
boundary_cst, cmp_code, visited_phis,
visited_flag_phis))
return false;
bitmap_clear_bit (*visited_flag_phis, SSA_NAME_VERSION (phi_result));
continue;
}
/* Now check if the constant is in the guarded range. */
if (is_value_included_in (flag_arg, boundary_cst, cmp_code))
{
/* Now that we know that this undefined edge is not pruned.
If the operand is defined by another phi, we can further
prune the incoming edges of that phi by checking
the predicates of this operands. */
tree opnd = gimple_phi_arg_def (phi, i);
gimple *opnd_def = SSA_NAME_DEF_STMT (opnd);
if (gphi *opnd_def_phi = dyn_cast <gphi *> (opnd_def))
{
unsigned opnds2 = m_eval.phi_arg_set (opnd_def_phi);
if (!MASK_EMPTY (opnds2))
{
edge opnd_edge = gimple_phi_arg_edge (phi, i);
if (def_preds.is_use_guarded (phi, opnd_edge->src,
opnd_def_phi, opnds2,
visited_phis))
return false;
}
}
else
return false;
}
}
return true;
}
/* Recursively compute the set PHI's incoming edges with "uninteresting"
operands of a phi chain, i.e., those for which EVAL returns false.
CD_ROOT is the control dependence root from which edges are collected
up the CFG nodes that it's dominated by. *EDGES holds the result, and
VISITED is used for detecting cycles. */
void
uninit_analysis::collect_phi_def_edges (gphi *phi, basic_block cd_root,
vec<edge> *edges,
hash_set<gimple *> *visited)
{
if (visited->elements () == 0
&& DEBUG_PREDICATE_ANALYZER
&& dump_file)
{
fprintf (dump_file, "%s for cd_root %u and ",
__func__, cd_root->index);
print_gimple_stmt (dump_file, phi, 0);
}
if (visited->add (phi))
return;
unsigned n = gimple_phi_num_args (phi);
unsigned opnds_arg_phi = m_eval.phi_arg_set (phi);
for (unsigned i = 0; i < n; i++)
{
if (!MASK_TEST_BIT (opnds_arg_phi, i))
{
/* Add the edge for a not maybe-undefined edge value. */
edge opnd_edge = gimple_phi_arg_edge (phi, i);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file,
"\tFound def edge %i -> %i for cd_root %i "
"and operand %u of: ",
opnd_edge->src->index, opnd_edge->dest->index,
cd_root->index, i);
print_gimple_stmt (dump_file, phi, 0);
}
edges->safe_push (opnd_edge);
continue;
}
else
{
tree opnd = gimple_phi_arg_def (phi, i);
if (TREE_CODE (opnd) == SSA_NAME)
{
gimple *def = SSA_NAME_DEF_STMT (opnd);
if (gimple_code (def) == GIMPLE_PHI
&& dominated_by_p (CDI_DOMINATORS, gimple_bb (def), cd_root))
/* Process PHI defs of maybe-undefined edge values
recursively. */
collect_phi_def_edges (as_a<gphi *> (def), cd_root, edges,
visited);
}
}
}
}
/* Return a bitset of all PHI arguments or zero if there are too many. */
unsigned
uninit_analysis::func_t::phi_arg_set (gphi *phi)
{
unsigned n = gimple_phi_num_args (phi);
if (max_phi_args < n)
return 0;
/* Set the least significant N bits. */
return (1U << n) - 1;
}
/* Determine if the predicate set of the use does not overlap with that
of the interesting paths. The most common senario of guarded use is
in Example 1:
Example 1:
if (some_cond)
{
x = ...; // set x to valid
flag = true;
}
... some code ...
if (flag)
use (x); // use when x is valid
The real world examples are usually more complicated, but similar
and usually result from inlining:
bool init_func (int * x)
{
if (some_cond)
return false;
*x = ...; // set *x to valid
return true;
}
void foo (..)
{
int x;
if (!init_func (&x))
return;
.. some_code ...
use (x); // use when x is valid
}
Another possible use scenario is in the following trivial example:
Example 2:
if (n > 0)
x = 1;
...
if (n > 0)
{
if (m < 2)
... = x;
}
Predicate analysis needs to compute the composite predicate:
1) 'x' use predicate: (n > 0) .AND. (m < 2)
2) 'x' default value (non-def) predicate: .NOT. (n > 0)
(the predicate chain for phi operand defs can be computed
starting from a bb that is control equivalent to the phi's
bb and is dominating the operand def.)
and check overlapping:
(n > 0) .AND. (m < 2) .AND. (.NOT. (n > 0))
<==> false
This implementation provides a framework that can handle different
scenarios. (Note that many simple cases are handled properly without
the predicate analysis if jump threading eliminates the merge point
thus makes path-sensitive analysis unnecessary.)
PHI is the phi node whose incoming (undefined) paths need to be
pruned, and OPNDS is the bitmap holding interesting operand
positions. VISITED is the pointer set of phi stmts being
checked. */
bool
uninit_analysis::overlap (gphi *phi, unsigned opnds, hash_set<gphi *> *visited,
const predicate &use_preds)
{
gimple *flag_def = NULL;
tree boundary_cst = NULL_TREE;
bitmap visited_flag_phis = NULL;
/* Find within the common prefix of multiple predicate chains
a predicate that is a comparison of a flag variable against
a constant. */
tree_code cmp_code = find_var_cmp_const (use_preds.chain (), phi, &flag_def,
&boundary_cst);
if (cmp_code == ERROR_MARK)
return true;
/* Now check all the uninit incoming edges have a constant flag
value that is in conflict with the use guard/predicate. */
gphi *phi_def = as_a<gphi *> (flag_def);
bool all_pruned = prune_phi_opnds (phi, opnds, phi_def, boundary_cst,
cmp_code, visited,
&visited_flag_phis);
if (visited_flag_phis)
BITMAP_FREE (visited_flag_phis);
return !all_pruned;
}
/* Return true if two predicates PRED1 and X2 are equivalent. Assume
the expressions have already properly re-associated. */
static inline bool
pred_equal_p (const pred_info &pred1, const pred_info &pred2)
{
if (!operand_equal_p (pred1.pred_lhs, pred2.pred_lhs, 0)
|| !operand_equal_p (pred1.pred_rhs, pred2.pred_rhs, 0))
return false;
tree_code c1 = pred1.cond_code, c2;
if (pred1.invert != pred2.invert
&& TREE_CODE_CLASS (pred2.cond_code) == tcc_comparison)
c2 = invert_tree_comparison (pred2.cond_code, false);
else
c2 = pred2.cond_code;
return c1 == c2;
}
/* Return true if PRED tests inequality (i.e., X != Y). */
static inline bool
is_neq_relop_p (const pred_info &pred)
{
return ((pred.cond_code == NE_EXPR && !pred.invert)
|| (pred.cond_code == EQ_EXPR && pred.invert));
}
/* Returns true if PRED is of the form X != 0. */
static inline bool
is_neq_zero_form_p (const pred_info &pred)
{
if (!is_neq_relop_p (pred) || !integer_zerop (pred.pred_rhs)
|| TREE_CODE (pred.pred_lhs) != SSA_NAME)
return false;
return true;
}
/* Return true if PRED is equivalent to X != 0. */
static inline bool
pred_expr_equal_p (const pred_info &pred, tree expr)
{
if (!is_neq_zero_form_p (pred))
return false;
return operand_equal_p (pred.pred_lhs, expr, 0);
}
/* Return true if VAL satisfies (x CMPC BOUNDARY) predicate. CMPC can
be either one of the range comparison codes ({GE,LT,EQ,NE}_EXPR and
the like), or BIT_AND_EXPR. EXACT_P is only meaningful for the latter.
Modify the question from VAL & BOUNDARY != 0 to VAL & BOUNDARY == VAL.
For other values of CMPC, EXACT_P is ignored. */
static bool
value_sat_pred_p (tree val, tree boundary, tree_code cmpc,
bool exact_p = false)
{
if (cmpc != BIT_AND_EXPR)
return is_value_included_in (val, boundary, cmpc);
wide_int andw = wi::to_wide (val) & wi::to_wide (boundary);
if (exact_p)
return andw == wi::to_wide (val);
return andw.to_uhwi ();
}
/* Return true if the domain of single predicate expression PRED1
is a subset of that of PRED2, and false if it cannot be proved. */
static bool
subset_of (const pred_info &pred1, const pred_info &pred2)
{
if (pred_equal_p (pred1, pred2))
return true;
if ((TREE_CODE (pred1.pred_rhs) != INTEGER_CST)
|| (TREE_CODE (pred2.pred_rhs) != INTEGER_CST))
return false;
if (!operand_equal_p (pred1.pred_lhs, pred2.pred_lhs, 0))
return false;
tree_code code1 = pred1.cond_code;
if (pred1.invert)
code1 = invert_tree_comparison (code1, false);
tree_code code2 = pred2.cond_code;
if (pred2.invert)
code2 = invert_tree_comparison (code2, false);
if (code2 == NE_EXPR && code1 == NE_EXPR)
return false;
if (code2 == NE_EXPR)
return !value_sat_pred_p (pred2.pred_rhs, pred1.pred_rhs, code1);
if (code1 == EQ_EXPR)
return value_sat_pred_p (pred1.pred_rhs, pred2.pred_rhs, code2);
if (code1 == code2)
return value_sat_pred_p (pred1.pred_rhs, pred2.pred_rhs, code2,
code1 == BIT_AND_EXPR);
return false;
}
/* Return true if the domain of CHAIN1 is a subset of that of CHAIN2.
Return false if it cannot be proven so. */
static bool
subset_of (const pred_chain &chain1, const pred_chain &chain2)
{
unsigned np1 = chain1.length ();
unsigned np2 = chain2.length ();
for (unsigned i2 = 0; i2 < np2; i2++)
{
bool found = false;
const pred_info &info2 = chain2[i2];
for (unsigned i1 = 0; i1 < np1; i1++)
{
const pred_info &info1 = chain1[i1];
if (subset_of (info1, info2))
{
found = true;
break;
}
}
if (!found)
return false;
}
return true;
}
/* Return true if the domain defined by the predicate chain PREDS is
a subset of the domain of *THIS. Return false if PREDS's domain
is not a subset of any of the sub-domains of *THIS (corresponding
to each individual chains in it), even though it may be still be
a subset of whole domain of *THIS which is the union (ORed) of all
its subdomains. In other words, the result is conservative. */
bool
predicate::includes (const pred_chain &chain) const
{
for (unsigned i = 0; i < m_preds.length (); i++)
if (subset_of (chain, m_preds[i]))
return true;
return false;
}
/* Return true if the domain defined by *THIS is a superset of PREDS's
domain.
Avoid building generic trees (and rely on the folding capability
of the compiler), and instead perform brute force comparison of
individual predicate chains (this won't be a computationally costly
since the chains are pretty short). Returning false does not
necessarily mean *THIS is not a superset of *PREDS, only that
it need not be since the analysis cannot prove it. */
bool
predicate::superset_of (const predicate &preds) const
{
for (unsigned i = 0; i < preds.m_preds.length (); i++)
if (!includes (preds.m_preds[i]))
return false;
return true;
}
/* Create a predicate of the form OP != 0 and push it the work list CHAIN. */
static void
push_to_worklist (tree op, pred_chain *chain, hash_set<tree> *mark_set)
{
if (mark_set->contains (op))
return;
mark_set->add (op);
pred_info arg_pred;
arg_pred.pred_lhs = op;
arg_pred.pred_rhs = integer_zero_node;
arg_pred.cond_code = NE_EXPR;
arg_pred.invert = false;
chain->safe_push (arg_pred);
}
/* Return a pred_info for a gimple assignment CMP_ASSIGN with comparison
rhs. */
static pred_info
get_pred_info_from_cmp (const gimple *cmp_assign)
{
pred_info pred;
pred.pred_lhs = gimple_assign_rhs1 (cmp_assign);
pred.pred_rhs = gimple_assign_rhs2 (cmp_assign);
pred.cond_code = gimple_assign_rhs_code (cmp_assign);
pred.invert = false;
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. */
static bool
is_degenerate_phi (gimple *phi, pred_info *pred)
{
tree op0 = gimple_phi_arg_def (phi, 0);
if (TREE_CODE (op0) != SSA_NAME)
return false;
gimple *def0 = SSA_NAME_DEF_STMT (op0);
if (gimple_code (def0) != GIMPLE_ASSIGN)
return false;
if (TREE_CODE_CLASS (gimple_assign_rhs_code (def0)) != tcc_comparison)
return false;
pred_info pred0 = get_pred_info_from_cmp (def0);
unsigned n = gimple_phi_num_args (phi);
for (unsigned i = 1; i < n; ++i)
{
tree op = gimple_phi_arg_def (phi, i);
if (TREE_CODE (op) != SSA_NAME)
return false;
gimple *def = SSA_NAME_DEF_STMT (op);
if (gimple_code (def) != GIMPLE_ASSIGN)
return false;
if (TREE_CODE_CLASS (gimple_assign_rhs_code (def)) != tcc_comparison)
return false;
pred_info pred = get_pred_info_from_cmp (def);
if (!pred_equal_p (pred, pred0))
return false;
}
*pred = pred0;
return true;
}
/* If compute_control_dep_chain bailed out due to limits this routine
tries to build a partial sparse path using dominators. Returns
path edges whose predicates are always true when reaching E. */
static void
simple_control_dep_chain (vec<edge>& chain, basic_block from, basic_block to)
{
if (!dominated_by_p (CDI_DOMINATORS, to, from))
return;
basic_block src = to;
while (src != from
&& chain.length () <= MAX_CHAIN_LEN)
{
basic_block dest = src;
src = get_immediate_dominator (CDI_DOMINATORS, src);
if (single_pred_p (dest))
{
edge pred_e = single_pred_edge (dest);
gcc_assert (pred_e->src == src);
if (!(pred_e->flags & ((EDGE_FAKE | EDGE_ABNORMAL | EDGE_DFS_BACK)))
&& !single_succ_p (src))
chain.safe_push (pred_e);
}
}
}
/* Perform a DFS walk on predecessor edges to mark the region denoted
by the EXIT_SRC block and DOM which dominates EXIT_SRC, including DOM.
Blocks in the region are marked with FLAG and added to BBS. BBS is
filled up to its capacity only after which the walk is terminated
and false is returned. If the whole region was marked, true is returned. */
static bool
dfs_mark_dominating_region (basic_block exit_src, basic_block dom, int flag,
vec<basic_block> &bbs)
{
if (exit_src == dom || exit_src->flags & flag)
return true;
if (!bbs.space (1))
return false;
bbs.quick_push (exit_src);
exit_src->flags |= flag;
auto_vec<edge_iterator, 20> stack (bbs.allocated () - bbs.length () + 1);
stack.quick_push (ei_start (exit_src->preds));
while (!stack.is_empty ())
{
/* Look at the edge on the top of the stack. */
edge_iterator ei = stack.last ();
basic_block src = ei_edge (ei)->src;
/* Check if the edge source has been visited yet. */
if (!(src->flags & flag))
{
/* Mark the source if there's still space. If not, return early. */
if (!bbs.space (1))
return false;
src->flags |= flag;
bbs.quick_push (src);
/* Queue its predecessors if we didn't reach DOM. */
if (src != dom && EDGE_COUNT (src->preds) > 0)
stack.quick_push (ei_start (src->preds));
}
else
{
if (!ei_one_before_end_p (ei))
ei_next (&stack.last ());
else
stack.pop ();
}
}
return true;
}
static bool
compute_control_dep_chain (basic_block dom_bb, const_basic_block dep_bb,
vec<edge> cd_chains[], unsigned *num_chains,
vec<edge> &cur_cd_chain, unsigned *num_calls,
unsigned in_region, unsigned depth,
bool *complete_p);
/* Helper for compute_control_dep_chain that walks the post-dominator
chain from CD_BB up unto TARGET_BB looking for paths to DEP_BB. */
static bool
compute_control_dep_chain_pdom (basic_block cd_bb, const_basic_block dep_bb,
basic_block target_bb,
vec<edge> cd_chains[], unsigned *num_chains,
vec<edge> &cur_cd_chain, unsigned *num_calls,
unsigned in_region, unsigned depth,
bool *complete_p)
{
bool found_cd_chain = false;
while (cd_bb != target_bb)
{
if (cd_bb == dep_bb)
{
/* Found a direct control dependence. */
if (*num_chains < MAX_NUM_CHAINS)
{
if (DEBUG_PREDICATE_ANALYZER && dump_file)
fprintf (dump_file, "%*s pushing { %s }\n",
depth, "", format_edge_vec (cur_cd_chain).c_str ());
cd_chains[*num_chains] = cur_cd_chain.copy ();
(*num_chains)++;
}
found_cd_chain = true;
/* Check path from next edge. */
break;
}
/* If the dominating region has been marked avoid walking outside. */
if (in_region != 0 && !(cd_bb->flags & in_region))
break;
/* Count the number of steps we perform to limit compile-time.
This should cover both recursion and the post-dominator walk. */
if (*num_calls > (unsigned)param_uninit_control_dep_attempts)
{
if (dump_file)
fprintf (dump_file, "param_uninit_control_dep_attempts "
"exceeded: %u\n", *num_calls);
*complete_p = false;
break;
}
++*num_calls;
/* Check if DEP_BB is indirectly control-dependent on DOM_BB. */
if (!single_succ_p (cd_bb)
&& compute_control_dep_chain (cd_bb, dep_bb, cd_chains,
num_chains, cur_cd_chain,
num_calls, in_region, depth + 1,
complete_p))
{
found_cd_chain = true;
break;
}
/* The post-dominator walk will reach a backedge only
from a forwarder, otherwise it should choose to exit
the SCC. */
if (single_succ_p (cd_bb)
&& single_succ_edge (cd_bb)->flags & EDGE_DFS_BACK)
break;
basic_block prev_cd_bb = cd_bb;
cd_bb = get_immediate_dominator (CDI_POST_DOMINATORS, cd_bb);
if (cd_bb == EXIT_BLOCK_PTR_FOR_FN (cfun))
break;
/* Pick up conditions toward the post dominator such like
loop exit conditions. See gcc.dg/uninit-pred-11.c and
gcc.dg/unninit-pred-12.c and PR106754. */
if (single_pred_p (cd_bb))
{
edge e2 = single_pred_edge (cd_bb);
gcc_assert (e2->src == prev_cd_bb);
/* But avoid adding fallthru or abnormal edges. */
if (!(e2->flags & (EDGE_FAKE | EDGE_ABNORMAL | EDGE_DFS_BACK))
&& !single_succ_p (prev_cd_bb))
cur_cd_chain.safe_push (e2);
}
}
return found_cd_chain;
}
/* Recursively compute the control dependence chains (paths of edges)
from the dependent basic block, DEP_BB, up to the dominating basic
block, DOM_BB (the head node of a chain should be dominated by it),
storing them in the CD_CHAINS array.
CUR_CD_CHAIN is the current chain being computed.
*NUM_CHAINS is total number of chains in the CD_CHAINS array.
*NUM_CALLS is the number of recursive calls to control unbounded
recursion.
Return true if the information is successfully computed, false if
there is no control dependence or not computed.
*COMPLETE_P is set to false if we stopped walking due to limits.
In this case there might be missing chains. */
static bool
compute_control_dep_chain (basic_block dom_bb, const_basic_block dep_bb,
vec<edge> cd_chains[], unsigned *num_chains,
vec<edge> &cur_cd_chain, unsigned *num_calls,
unsigned in_region, unsigned depth,
bool *complete_p)
{
/* In our recursive calls this doesn't happen. */
if (single_succ_p (dom_bb))
return false;
/* FIXME: Use a set instead. */
unsigned cur_chain_len = cur_cd_chain.length ();
if (cur_chain_len > MAX_CHAIN_LEN)
{
if (dump_file)
fprintf (dump_file, "MAX_CHAIN_LEN exceeded: %u\n", cur_chain_len);
*complete_p = false;
return false;
}
if (cur_chain_len > 5)
{
if (dump_file)
fprintf (dump_file, "chain length exceeds 5: %u\n", cur_chain_len);
}
if (DEBUG_PREDICATE_ANALYZER && dump_file)
fprintf (dump_file,
"%*s%s (dom_bb = %u, dep_bb = %u, ..., "
"cur_cd_chain = { %s }, ...)\n",
depth, "", __func__, dom_bb->index, dep_bb->index,
format_edge_vec (cur_cd_chain).c_str ());
bool found_cd_chain = false;
/* Iterate over DOM_BB's successors. */
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, dom_bb->succs)
{
if (e->flags & (EDGE_FAKE | EDGE_ABNORMAL | EDGE_DFS_BACK))
continue;
basic_block cd_bb = e->dest;
unsigned pop_mark = cur_cd_chain.length ();
cur_cd_chain.safe_push (e);
basic_block target_bb
= get_immediate_dominator (CDI_POST_DOMINATORS, dom_bb);
/* Walk the post-dominator chain up to the CFG merge. */
found_cd_chain
|= compute_control_dep_chain_pdom (cd_bb, dep_bb, target_bb,
cd_chains, num_chains,
cur_cd_chain, num_calls,
in_region, depth, complete_p);
cur_cd_chain.truncate (pop_mark);
gcc_assert (cur_cd_chain.length () == cur_chain_len);
}
gcc_assert (cur_cd_chain.length () == cur_chain_len);
return found_cd_chain;
}
/* Wrapper around the compute_control_dep_chain worker above. Returns
true when the collected set of chains in CD_CHAINS is complete. */
static bool
compute_control_dep_chain (basic_block dom_bb, const_basic_block dep_bb,
vec<edge> cd_chains[], unsigned *num_chains,
unsigned in_region = 0)
{
auto_vec<edge, MAX_CHAIN_LEN + 1> cur_cd_chain;
unsigned num_calls = 0;
unsigned depth = 0;
bool complete_p = true;
/* Walk the post-dominator chain. */
compute_control_dep_chain_pdom (dom_bb, dep_bb, NULL, cd_chains,
num_chains, cur_cd_chain, &num_calls,
in_region, depth, &complete_p);
return complete_p;
}
/* Implemented simplifications:
1) ((x IOR y) != 0) AND (x != 0) is equivalent to (x != 0);
2) (X AND Y) OR (!X AND Y) is equivalent to Y;
3) X OR (!X AND Y) is equivalent to (X OR Y);
4) ((x IAND y) != 0) || (x != 0 AND y != 0)) is equivalent to
(x != 0 AND y != 0)
5) (X AND Y) OR (!X AND Z) OR (!Y AND Z) is equivalent to
(X AND Y) OR Z
PREDS is the predicate chains, and N is the number of chains. */
/* Implement rule 1 above. PREDS is the AND predicate to simplify
in place. */
static void
simplify_1 (pred_chain &chain)
{
bool simplified = false;
pred_chain s_chain = vNULL;
unsigned n = chain.length ();
for (unsigned i = 0; i < n; i++)
{
pred_info &a_pred = chain[i];
if (!a_pred.pred_lhs
|| !is_neq_zero_form_p (a_pred))
continue;
gimple *def_stmt = SSA_NAME_DEF_STMT (a_pred.pred_lhs);
if (gimple_code (def_stmt) != GIMPLE_ASSIGN)
continue;
if (gimple_assign_rhs_code (def_stmt) != BIT_IOR_EXPR)
continue;
for (unsigned j = 0; j < n; j++)
{
const pred_info &b_pred = chain[j];
if (!b_pred.pred_lhs
|| !is_neq_zero_form_p (b_pred))
continue;
if (pred_expr_equal_p (b_pred, gimple_assign_rhs1 (def_stmt))
|| pred_expr_equal_p (b_pred, gimple_assign_rhs2 (def_stmt)))
{
/* Mark A_PRED for removal from PREDS. */
a_pred.pred_lhs = NULL;
a_pred.pred_rhs = NULL;
simplified = true;
break;
}
}
}
if (!simplified)
return;
/* Remove predicates marked above. */
for (unsigned i = 0; i < n; i++)
{
pred_info &a_pred = chain[i];
if (!a_pred.pred_lhs)
continue;
s_chain.safe_push (a_pred);
}
chain.release ();
chain = s_chain;
}
/* Implements rule 2 for the OR predicate PREDS:
2) (X AND Y) OR (!X AND Y) is equivalent to Y. */
bool
predicate::simplify_2 ()
{
bool simplified = false;
/* (X AND Y) OR (!X AND Y) is equivalent to Y.
(X AND Y) OR (X AND !Y) is equivalent to X. */
unsigned n = m_preds.length ();
for (unsigned i = 0; i < n; i++)
{
pred_chain &a_chain = m_preds[i];
if (a_chain.length () != 2)
continue;
/* Create copies since the chain may be released below before
the copy is added to the other chain. */
const pred_info x = a_chain[0];
const pred_info y = a_chain[1];
for (unsigned j = 0; j < n; j++)
{
if (j == i)
continue;
pred_chain &b_chain = m_preds[j];
if (b_chain.length () != 2)
continue;
const pred_info &x2 = b_chain[0];
const pred_info &y2 = b_chain[1];
if (pred_equal_p (x, x2) && pred_neg_p (y, y2))
{
/* Kill a_chain. */
b_chain.release ();
a_chain.release ();
b_chain.safe_push (x);
simplified = true;
break;
}
if (pred_neg_p (x, x2) && pred_equal_p (y, y2))
{
/* Kill a_chain. */
a_chain.release ();
b_chain.release ();
b_chain.safe_push (y);
simplified = true;
break;
}
}
}
/* Now clean up the chain. */
if (simplified)
{
pred_chain_union s_preds = vNULL;
for (unsigned i = 0; i < n; i++)
{
if (m_preds[i].is_empty ())
continue;
s_preds.safe_push (m_preds[i]);
}
m_preds.release ();
m_preds = s_preds;
s_preds = vNULL;
}
return simplified;
}
/* Implement rule 3 for the OR predicate PREDS:
3) x OR (!x AND y) is equivalent to x OR y. */
bool
predicate::simplify_3 ()
{
/* Now iteratively simplify X OR (!X AND Z ..)
into X OR (Z ...). */
unsigned n = m_preds.length ();
if (n < 2)
return false;
bool simplified = false;
for (unsigned i = 0; i < n; i++)
{
const pred_chain &a_chain = m_preds[i];
if (a_chain.length () != 1)
continue;
const pred_info &x = a_chain[0];
for (unsigned j = 0; j < n; j++)
{
if (j == i)
continue;
pred_chain b_chain = m_preds[j];
if (b_chain.length () < 2)
continue;
for (unsigned k = 0; k < b_chain.length (); k++)
{
const pred_info &x2 = b_chain[k];
if (pred_neg_p (x, x2))
{
b_chain.unordered_remove (k);
simplified = true;
break;
}
}
}
}
return simplified;
}
/* Implement rule 4 for the OR predicate PREDS:
2) ((x AND y) != 0) OR (x != 0 AND y != 0) is equivalent to
(x != 0 ANd y != 0). */
bool
predicate::simplify_4 ()
{
bool simplified = false;
pred_chain_union s_preds = vNULL;
unsigned n = m_preds.length ();
for (unsigned i = 0; i < n; i++)
{
pred_chain a_chain = m_preds[i];
if (a_chain.length () != 1)
continue;
const pred_info &z = a_chain[0];
if (!is_neq_zero_form_p (z))
continue;
gimple *def_stmt = SSA_NAME_DEF_STMT (z.pred_lhs);
if (gimple_code (def_stmt) != GIMPLE_ASSIGN)
continue;
if (gimple_assign_rhs_code (def_stmt) != BIT_AND_EXPR)
continue;
for (unsigned j = 0; j < n; j++)
{
if (j == i)
continue;
pred_chain b_chain = m_preds[j];
if (b_chain.length () != 2)
continue;
const pred_info &x2 = b_chain[0];
const pred_info &y2 = b_chain[1];
if (!is_neq_zero_form_p (x2) || !is_neq_zero_form_p (y2))
continue;
if ((pred_expr_equal_p (x2, gimple_assign_rhs1 (def_stmt))
&& pred_expr_equal_p (y2, gimple_assign_rhs2 (def_stmt)))
|| (pred_expr_equal_p (x2, gimple_assign_rhs2 (def_stmt))
&& pred_expr_equal_p (y2, gimple_assign_rhs1 (def_stmt))))
{
/* Kill a_chain. */
a_chain.release ();
simplified = true;
break;
}
}
}
/* Now clean up the chain. */
if (simplified)
{
for (unsigned i = 0; i < n; i++)
{
if (m_preds[i].is_empty ())
continue;
s_preds.safe_push (m_preds[i]);
}
m_preds.release ();
m_preds = s_preds;
s_preds = vNULL;
}
return simplified;
}
/* Simplify predicates in *THIS. */
void
predicate::simplify (gimple *use_or_def, bool is_use)
{
if (dump_file && dump_flags & TDF_DETAILS)
{
fprintf (dump_file, "Before simplication ");
dump (dump_file, use_or_def, is_use ? "[USE]:\n" : "[DEF]:\n");
}
unsigned n = m_preds.length ();
for (unsigned i = 0; i < n; i++)
::simplify_1 (m_preds[i]);
if (n < 2)
return;
bool changed;
do
{
changed = false;
if (simplify_2 ())
changed = true;
if (simplify_3 ())
changed = true;
if (simplify_4 ())
changed = true;
}
while (changed);
}
/* Attempt to normalize predicate chains by following UD chains by
building up a big tree of either IOR operations or AND operations,
and converting the IOR tree into a pred_chain_union or the BIT_AND
tree into a pred_chain.
Example:
_3 = _2 RELOP1 _1;
_6 = _5 RELOP2 _4;
_9 = _8 RELOP3 _7;
_10 = _3 | _6;
_12 = _9 | _0;
_t = _10 | _12;
then _t != 0 will be normalized into a pred_chain_union
(_2 RELOP1 _1) OR (_5 RELOP2 _4) OR (_8 RELOP3 _7) OR (_0 != 0)
Similarly given:
_3 = _2 RELOP1 _1;
_6 = _5 RELOP2 _4;
_9 = _8 RELOP3 _7;
_10 = _3 & _6;
_12 = _9 & _0;
then _t != 0 will be normalized into a pred_chain:
(_2 RELOP1 _1) AND (_5 RELOP2 _4) AND (_8 RELOP3 _7) AND (_0 != 0)
*/
/* Normalize predicate PRED:
1) if PRED can no longer be normalized, append it to *THIS.
2) otherwise if PRED is of the form x != 0, follow x's definition
and put normalized predicates into WORK_LIST. */
void
predicate::normalize (pred_chain *norm_chain,
pred_info pred,
tree_code and_or_code,
pred_chain *work_list,
hash_set<tree> *mark_set)
{
if (!is_neq_zero_form_p (pred))
{
if (and_or_code == BIT_IOR_EXPR)
push_pred (pred);
else
norm_chain->safe_push (pred);
return;
}
gimple *def_stmt = SSA_NAME_DEF_STMT (pred.pred_lhs);
if (gimple_code (def_stmt) == GIMPLE_PHI
&& is_degenerate_phi (def_stmt, &pred))
/* PRED has been modified above. */
work_list->safe_push (pred);
else if (gimple_code (def_stmt) == GIMPLE_PHI && and_or_code == BIT_IOR_EXPR)
{
unsigned n = gimple_phi_num_args (def_stmt);
/* Punt for a nonzero constant. The predicate should be one guarding
the phi edge. */
for (unsigned i = 0; i < n; ++i)
{
tree op = gimple_phi_arg_def (def_stmt, i);
if (TREE_CODE (op) == INTEGER_CST && !integer_zerop (op))
{
push_pred (pred);
return;
}
}
for (unsigned i = 0; i < n; ++i)
{
tree op = gimple_phi_arg_def (def_stmt, i);
if (integer_zerop (op))
continue;
push_to_worklist (op, work_list, mark_set);
}
}
else if (gimple_code (def_stmt) != GIMPLE_ASSIGN)
{
if (and_or_code == BIT_IOR_EXPR)
push_pred (pred);
else
norm_chain->safe_push (pred);
}
else if (gimple_assign_rhs_code (def_stmt) == and_or_code)
{
/* Avoid splitting up bit manipulations like x & 3 or y | 1. */
if (is_gimple_min_invariant (gimple_assign_rhs2 (def_stmt)))
{
/* But treat x & 3 as a condition. */
if (and_or_code == BIT_AND_EXPR)
{
pred_info n_pred;
n_pred.pred_lhs = gimple_assign_rhs1 (def_stmt);
n_pred.pred_rhs = gimple_assign_rhs2 (def_stmt);
n_pred.cond_code = and_or_code;
n_pred.invert = false;
norm_chain->safe_push (n_pred);
}
}
else
{
push_to_worklist (gimple_assign_rhs1 (def_stmt), work_list, mark_set);
push_to_worklist (gimple_assign_rhs2 (def_stmt), work_list, mark_set);
}
}
else if (TREE_CODE_CLASS (gimple_assign_rhs_code (def_stmt))
== tcc_comparison)
{
pred_info n_pred = get_pred_info_from_cmp (def_stmt);
if (and_or_code == BIT_IOR_EXPR)
push_pred (n_pred);
else
norm_chain->safe_push (n_pred);
}
else
{
if (and_or_code == BIT_IOR_EXPR)
push_pred (pred);
else
norm_chain->safe_push (pred);
}
}
/* Normalize PRED and store the normalized predicates in THIS->M_PREDS. */
void
predicate::normalize (const pred_info &pred)
{
if (!is_neq_zero_form_p (pred))
{
push_pred (pred);
return;
}
tree_code and_or_code = ERROR_MARK;
gimple *def_stmt = SSA_NAME_DEF_STMT (pred.pred_lhs);
if (gimple_code (def_stmt) == GIMPLE_ASSIGN)
and_or_code = gimple_assign_rhs_code (def_stmt);
if (and_or_code != BIT_IOR_EXPR && and_or_code != BIT_AND_EXPR)
{
if (TREE_CODE_CLASS (and_or_code) == tcc_comparison)
{
pred_info n_pred = get_pred_info_from_cmp (def_stmt);
push_pred (n_pred);
}
else
push_pred (pred);
return;
}
pred_chain norm_chain = vNULL;
pred_chain work_list = vNULL;
work_list.safe_push (pred);
hash_set<tree> mark_set;
while (!work_list.is_empty ())
{
pred_info a_pred = work_list.pop ();
normalize (&norm_chain, a_pred, and_or_code, &work_list, &mark_set);
}
if (and_or_code == BIT_AND_EXPR)
m_preds.safe_push (norm_chain);
work_list.release ();
}
/* Normalize a single predicate PRED_CHAIN and append it to *THIS. */
void
predicate::normalize (const pred_chain &chain)
{
pred_chain work_list = vNULL;
hash_set<tree> mark_set;
for (unsigned i = 0; i < chain.length (); i++)
{
work_list.safe_push (chain[i]);
mark_set.add (chain[i].pred_lhs);
}
/* Normalized chain of predicates built up below. */
pred_chain norm_chain = vNULL;
while (!work_list.is_empty ())
{
pred_info pi = work_list.pop ();
predicate pred;
/* The predicate object is not modified here, only NORM_CHAIN and
WORK_LIST are appended to. */
pred.normalize (&norm_chain, pi, BIT_AND_EXPR, &work_list, &mark_set);
}
m_preds.safe_push (norm_chain);
work_list.release ();
}
/* Normalize predicate chains in THIS. */
void
predicate::normalize (gimple *use_or_def, bool is_use)
{
if (dump_file && dump_flags & TDF_DETAILS)
{
fprintf (dump_file, "Before normalization ");
dump (dump_file, use_or_def, is_use ? "[USE]:\n" : "[DEF]:\n");
}
predicate norm_preds;
for (unsigned i = 0; i < m_preds.length (); i++)
{
if (m_preds[i].length () != 1)
norm_preds.normalize (m_preds[i]);
else
norm_preds.normalize (m_preds[i][0]);
}
*this = norm_preds;
if (dump_file)
{
fprintf (dump_file, "After normalization ");
dump (dump_file, use_or_def, is_use ? "[USE]:\n" : "[DEF]:\n");
}
}
/* Convert the chains of control dependence edges into a set of predicates.
A control dependence chain is represented by a vector edges. DEP_CHAINS
points to an array of NUM_CHAINS dependence chains. One edge in
a dependence chain is mapped to predicate expression represented by
pred_info type. One dependence chain is converted to a composite
predicate that is the result of AND operation of pred_info mapped to
each edge. A composite predicate is represented by a vector of
pred_info. Sets M_PREDS to the resulting composite predicates. */
void
predicate::init_from_control_deps (const vec<edge> *dep_chains,
unsigned num_chains, bool is_use)
{
gcc_assert (is_empty ());
if (num_chains == 0)
return;
if (DEBUG_PREDICATE_ANALYZER && dump_file)
fprintf (dump_file, "init_from_control_deps [%s] {%s}:\n",
is_use ? "USE" : "DEF",
format_edge_vecs (dep_chains, num_chains).c_str ());
/* Convert the control dependency chain into a set of predicates. */
m_preds.reserve (num_chains);
for (unsigned i = 0; i < num_chains; i++)
{
/* One path through the CFG represents a logical conjunction
of the predicates. */
const vec<edge> &path = dep_chains[i];
bool has_valid_pred = false;
/* The chain of predicates guarding the definition along this path. */
pred_chain t_chain{ };
for (unsigned j = 0; j < path.length (); j++)
{
edge e = path[j];
basic_block guard_bb = e->src;
gcc_assert (!empty_block_p (guard_bb) && !single_succ_p (guard_bb));
/* Skip this edge if it is bypassing an abort - when the
condition is not satisfied we are neither reaching the
definition nor the use so it isn't meaningful. Note if
we are processing the use predicate the condition is
meaningful. See PR65244. */
if (!is_use && EDGE_COUNT (e->src->succs) == 2)
{
edge e1;
edge_iterator ei1;
bool skip = false;
FOR_EACH_EDGE (e1, ei1, e->src->succs)
{
if (EDGE_COUNT (e1->dest->succs) == 0)
{
skip = true;
break;
}
}
if (skip)
{
has_valid_pred = true;
continue;
}
}
/* Get the conditional controlling the bb exit edge. */
gimple *cond_stmt = last_stmt (guard_bb);
if (gimple_code (cond_stmt) == GIMPLE_COND)
{
/* 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);
t_chain.safe_push (one_pred);
if (DEBUG_PREDICATE_ANALYZER && dump_file)
{
fprintf (dump_file, "%d -> %d: one_pred = ",
e->src->index, e->dest->index);
dump_pred_info (dump_file, one_pred);
fputc ('\n', dump_file);
}
has_valid_pred = true;
}
else if (gswitch *gs = dyn_cast<gswitch *> (cond_stmt))
{
/* Find the case label, but avoid quadratic behavior. */
tree l = get_cases_for_edge (e, gs);
/* If more than one label reaches this block or the case
label doesn't have a contiguous range of values (like the
default one) fail. */
if (!l || CASE_CHAIN (l) || !CASE_LOW (l))
has_valid_pred = false;
else if (!CASE_HIGH (l)
|| operand_equal_p (CASE_LOW (l), CASE_HIGH (l)))
{
pred_info one_pred;
one_pred.pred_lhs = gimple_switch_index (gs);
one_pred.pred_rhs = CASE_LOW (l);
one_pred.cond_code = EQ_EXPR;
one_pred.invert = false;
t_chain.safe_push (one_pred);
has_valid_pred = true;
}
else
{
/* Support a case label with a range with
two predicates. We're overcommitting on
the MAX_CHAIN_LEN budget by at most a factor
of two here. */
pred_info one_pred;
one_pred.pred_lhs = gimple_switch_index (gs);
one_pred.pred_rhs = CASE_LOW (l);
one_pred.cond_code = GE_EXPR;
one_pred.invert = false;
t_chain.safe_push (one_pred);
one_pred.pred_rhs = CASE_HIGH (l);
one_pred.cond_code = LE_EXPR;
t_chain.safe_push (one_pred);
has_valid_pred = true;
}
}
else if (stmt_can_throw_internal (cfun, cond_stmt)
&& !(e->flags & EDGE_EH))
/* Ignore the exceptional control flow and proceed as if
E were a fallthru without a controlling predicate for
both the USE (valid) and DEF (questionable) case. */
has_valid_pred = true;
else
has_valid_pred = false;
/* For USE predicates we can drop components of the
AND chain. */
if (!has_valid_pred && !is_use)
break;
}
/* For DEF predicates we have to drop components of the OR chain
on failure. */
if (!has_valid_pred && !is_use)
{
t_chain.release ();
continue;
}
/* When we add || 1 simply prune the chain and return. */
if (t_chain.is_empty ())
{
t_chain.release ();
for (auto chain : m_preds)
chain.release ();
m_preds.truncate (0);
break;
}
m_preds.quick_push (t_chain);
}
if (DEBUG_PREDICATE_ANALYZER && dump_file)
dump (dump_file);
}
/* Store a PRED in *THIS. */
void
predicate::push_pred (const pred_info &pred)
{
pred_chain chain = vNULL;
chain.safe_push (pred);
m_preds.safe_push (chain);
}
/* Dump predicates in *THIS to F. */
void
predicate::dump (FILE *f) const
{
unsigned np = m_preds.length ();
if (np == 0)
{
fprintf (f, "\tTRUE (empty)\n");
return;
}
for (unsigned i = 0; i < np; i++)
{
if (i > 0)
fprintf (f, "\tOR (");
else
fprintf (f, "\t(");
dump_pred_chain (f, m_preds[i]);
fprintf (f, ")\n");
}
}
/* Dump predicates in *THIS to stderr. */
void
predicate::debug () const
{
dump (stderr);
}
/* Dump predicates in *THIS for STMT prepended by MSG to F. */
void
predicate::dump (FILE *f, gimple *stmt, const char *msg) const
{
fprintf (f, "%s", msg);
if (stmt)
{
fputc ('\t', f);
print_gimple_stmt (f, stmt, 0);
fprintf (f, " is conditional on:\n");
}
dump (f);
}
/* Initialize USE_PREDS with the predicates of the control dependence chains
between the basic block DEF_BB that defines a variable of interst and
USE_BB that uses the variable, respectively. */
bool
uninit_analysis::init_use_preds (predicate &use_preds, basic_block def_bb,
basic_block use_bb)
{
if (DEBUG_PREDICATE_ANALYZER && dump_file)
fprintf (dump_file, "init_use_preds (def_bb = %u, use_bb = %u)\n",
def_bb->index, use_bb->index);
gcc_assert (use_preds.is_empty ()
&& dominated_by_p (CDI_DOMINATORS, use_bb, def_bb));
/* Set CD_ROOT to the basic block closest to USE_BB that is the control
equivalent of (is guarded by the same predicate as) DEF_BB that also
dominates USE_BB. This mimics the inner loop in
compute_control_dep_chain. */
basic_block cd_root = def_bb;
do
{
basic_block pdom = get_immediate_dominator (CDI_POST_DOMINATORS, cd_root);
/* Stop at a loop exit which is also postdominating cd_root. */
if (single_pred_p (pdom) && !single_succ_p (cd_root))
break;
if (!dominated_by_p (CDI_DOMINATORS, pdom, cd_root)
|| !dominated_by_p (CDI_DOMINATORS, use_bb, pdom))
break;
cd_root = pdom;
}
while (1);
auto_bb_flag in_region (cfun);
auto_vec<basic_block, 20> region (MIN (n_basic_blocks_for_fn (cfun),
param_uninit_control_dep_attempts));
/* Set DEP_CHAINS to the set of edges between CD_ROOT and USE_BB.
Each DEP_CHAINS element is a series of edges whose conditions
are logical conjunctions. Together, the DEP_CHAINS vector is
used below to initialize an OR expression of the conjunctions. */
unsigned num_chains = 0;
auto_vec<edge> dep_chains[MAX_NUM_CHAINS];
if (!dfs_mark_dominating_region (use_bb, cd_root, in_region, region)
|| !compute_control_dep_chain (cd_root, use_bb, dep_chains, &num_chains,
in_region))
{
/* If the info in dep_chains is not complete we need to use a
conservative approximation for the use predicate. */
if (DEBUG_PREDICATE_ANALYZER && dump_file)
fprintf (dump_file, "init_use_preds: dep_chain incomplete, using "
"conservative approximation\n");
num_chains = 1;
dep_chains[0].truncate (0);
simple_control_dep_chain (dep_chains[0], cd_root, use_bb);
}
/* Unmark the region. */
for (auto bb : region)
bb->flags &= ~in_region;
/* From the set of edges computed above initialize *THIS as the OR
condition under which the definition in DEF_BB is used in USE_BB.
Each OR subexpression is represented by one element of DEP_CHAINS,
where each element consists of a series of AND subexpressions. */
use_preds.init_from_control_deps (dep_chains, num_chains, true);
return !use_preds.is_empty ();
}
/* Release resources in *THIS. */
predicate::~predicate ()
{
unsigned n = m_preds.length ();
for (unsigned i = 0; i != n; ++i)
m_preds[i].release ();
m_preds.release ();
}
/* Copy-assign RHS to *THIS. */
predicate&
predicate::operator= (const predicate &rhs)
{
if (this == &rhs)
return *this;
unsigned n = m_preds.length ();
for (unsigned i = 0; i != n; ++i)
m_preds[i].release ();
m_preds.release ();
n = rhs.m_preds.length ();
for (unsigned i = 0; i != n; ++i)
{
const pred_chain &chain = rhs.m_preds[i];
m_preds.safe_push (chain.copy ());
}
return *this;
}
/* For each use edge of PHI, compute all control dependence chains
and convert those to the composite predicates in M_PREDS.
Return true if a nonempty predicate has been obtained. */
bool
uninit_analysis::init_from_phi_def (gphi *phi)
{
gcc_assert (m_phi_def_preds.is_empty ());
basic_block phi_bb = gimple_bb (phi);
/* Find the closest dominating bb to be the control dependence root. */
basic_block cd_root = get_immediate_dominator (CDI_DOMINATORS, phi_bb);
if (!cd_root)
return false;
/* Set DEF_EDGES to the edges to the PHI from the bb's that provide
definitions of each of the PHI operands for which M_EVAL is false. */
auto_vec<edge> def_edges;
hash_set<gimple *> visited_phis;
collect_phi_def_edges (phi, cd_root, &def_edges, &visited_phis);
unsigned nedges = def_edges.length ();
if (nedges == 0)
return false;
auto_bb_flag in_region (cfun);
auto_vec<basic_block, 20> region (MIN (n_basic_blocks_for_fn (cfun),
param_uninit_control_dep_attempts));
/* Pre-mark the PHI incoming edges PHI block to make sure we only walk
interesting edges from there. */
for (unsigned i = 0; i < nedges; i++)
{
if (!(def_edges[i]->dest->flags & in_region))
{
if (!region.space (1))
break;
def_edges[i]->dest->flags |= in_region;
region.quick_push (def_edges[i]->dest);
}
}
for (unsigned i = 0; i < nedges; i++)
if (!dfs_mark_dominating_region (def_edges[i]->src, cd_root,
in_region, region))
break;
unsigned num_chains = 0;
auto_vec<edge> dep_chains[MAX_NUM_CHAINS];
for (unsigned i = 0; i < nedges; i++)
{
edge e = def_edges[i];
unsigned prev_nc = num_chains;
bool complete_p = compute_control_dep_chain (cd_root, e->src, dep_chains,
&num_chains, in_region);
/* Update the newly added chains with the phi operand edge. */
if (EDGE_COUNT (e->src->succs) > 1)
{
if (complete_p
&& prev_nc == num_chains
&& num_chains < MAX_NUM_CHAINS)
/* We can only add a chain for the PHI operand edge when the
collected info was complete, otherwise the predicate may
not be conservative. */
dep_chains[num_chains++] = vNULL;
for (unsigned j = prev_nc; j < num_chains; j++)
dep_chains[j].safe_push (e);
}
}
/* Unmark the region. */
for (auto bb : region)
bb->flags &= ~in_region;
/* Convert control dependence chains to the predicate in *THIS under
which the PHI operands are defined to values for which M_EVAL is
false. */
m_phi_def_preds.init_from_control_deps (dep_chains, num_chains, false);
return !m_phi_def_preds.is_empty ();
}
/* Compute the predicates that guard the use USE_STMT and check if
the incoming paths that have an empty (or possibly empty) definition
can be pruned. Return true if it can be determined that the use of
PHI's def in USE_STMT is guarded by a predicate set that does not
overlap with the predicate sets of all runtime paths that do not
have a definition.
Return false if the use is not guarded or if it cannot be determined.
USE_BB is the bb of the use (for phi operand use, the bb is not the bb
of the phi stmt, but the source bb of the operand edge).
OPNDS is a bitmap with a bit set for each PHI operand of interest.
THIS->M_PREDS contains the (memoized) defining predicate chains of
a PHI. If THIS->M_PREDS is empty, the PHI's defining predicate
chains are computed and stored into THIS->M_PREDS as needed.
VISITED_PHIS is a pointer set of phis being visited. */
bool
uninit_analysis::is_use_guarded (gimple *use_stmt, basic_block use_bb,
gphi *phi, unsigned opnds,
hash_set<gphi *> *visited)
{
if (visited->add (phi))
return false;
/* The basic block where the PHI is defined. */
basic_block def_bb = gimple_bb (phi);
/* Try to build the predicate expression under which the PHI flows
into its use. This will be empty if the PHI is defined and used
in the same bb. */
predicate use_preds;
if (!init_use_preds (use_preds, def_bb, use_bb))
return false;
use_preds.simplify (use_stmt, /*is_use=*/true);
use_preds.normalize (use_stmt, /*is_use=*/true);
/* Try to prune the dead incoming phi edges. */
if (!overlap (phi, opnds, visited, use_preds))
{
if (DEBUG_PREDICATE_ANALYZER && dump_file)
fputs ("found predicate overlap\n", dump_file);
return true;
}
if (m_phi_def_preds.is_empty ())
{
/* Lazily initialize *THIS from PHI. */
if (!init_from_phi_def (phi))
return false;
m_phi_def_preds.simplify (phi);
m_phi_def_preds.normalize (phi);
}
/* 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))
return true;
return false;
}
/* Public interface to the above. */
bool
uninit_analysis::is_use_guarded (gimple *stmt, basic_block use_bb, gphi *phi,
unsigned opnds)
{
hash_set<gphi *> visited;
return is_use_guarded (stmt, use_bb, phi, opnds, &visited);
}