/* Predicate aware uninitialized variable warning.
- Copyright (C) 2001-2017 Free Software Foundation, Inc.
+ Copyright (C) 2001-2020 Free Software Foundation, Inc.
Contributed by Xinliang David Li <davidxl@google.com>
This file is part of GCC.
#include "fold-const.h"
#include "gimple-iterator.h"
#include "tree-ssa.h"
-#include "params.h"
#include "tree-cfg.h"
+#include "cfghooks.h"
/* This implements the pass that does predicate aware warning on uses of
possibly uninitialized variables. The pass first collects the set of
cfun_loc = DECL_SOURCE_LOCATION (cfun->decl);
xloc = expand_location (location);
floc = expand_location (cfun_loc);
+ auto_diagnostic_group d;
if (warning_at (location, wc, gmsgid, expr))
{
TREE_NO_WARNING (expr) = 1;
&& gimple_has_location (stmt))
{
tree rhs = gimple_assign_rhs1 (stmt);
+ tree lhs = gimple_assign_lhs (stmt);
+ bool has_bit_insert = false;
+ use_operand_p luse_p;
+ imm_use_iterator liter;
+
if (TREE_NO_WARNING (rhs))
continue;
ao_ref ref;
ao_ref_init (&ref, rhs);
- /* Do not warn if it can be initialized outside this function. */
+ /* Do not warn if the base was marked so or this is a
+ hard register var. */
tree base = ao_ref_base (&ref);
- if (!VAR_P (base)
- || DECL_HARD_REGISTER (base)
- || is_global_var (base)
+ if ((VAR_P (base)
+ && DECL_HARD_REGISTER (base))
|| TREE_NO_WARNING (base))
continue;
/* Do not warn if the access is fully outside of the
variable. */
- if (ref.size != -1
- && ref.max_size == ref.size
- && (ref.offset + ref.size <= 0
- || (ref.offset >= 0
- && TREE_CODE (DECL_SIZE (base)) == INTEGER_CST
- && compare_tree_int (DECL_SIZE (base),
- ref.offset) <= 0)))
+ poly_int64 decl_size;
+ if (DECL_P (base)
+ && known_size_p (ref.size)
+ && ((known_eq (ref.max_size, ref.size)
+ && known_le (ref.offset + ref.size, 0))
+ || (known_ge (ref.offset, 0)
+ && DECL_SIZE (base)
+ && poly_int_tree_p (DECL_SIZE (base), &decl_size)
+ && known_le (decl_size, ref.offset))))
+ continue;
+
+ /* Do not warn if the access is then used for a BIT_INSERT_EXPR. */
+ if (TREE_CODE (lhs) == SSA_NAME)
+ FOR_EACH_IMM_USE_FAST (luse_p, liter, lhs)
+ {
+ gimple *use_stmt = USE_STMT (luse_p);
+ /* BIT_INSERT_EXPR first operand should not be considered
+ a use for the purpose of uninit warnings. */
+ if (gassign *ass = dyn_cast <gassign *> (use_stmt))
+ {
+ if (gimple_assign_rhs_code (ass) == BIT_INSERT_EXPR
+ && luse_p->use == gimple_assign_rhs1_ptr (ass))
+ {
+ has_bit_insert = true;
+ break;
+ }
+ }
+ }
+ if (has_bit_insert)
continue;
/* Limit the walking to a constant number of stmts after
&& oracle_cnt > vdef_cnt * 2)
limit = 32;
check_defs_data data;
+ bool fentry_reached = false;
data.found_may_defs = false;
use = gimple_vuse (stmt);
int res = walk_aliased_vdefs (&ref, use,
check_defs, &data, NULL,
- NULL, limit);
+ &fentry_reached, limit);
if (res == -1)
{
oracle_cnt += limit;
oracle_cnt += res;
if (data.found_may_defs)
continue;
+ /* Do not warn if it can be initialized outside this function.
+ If we did not reach function entry then we found killing
+ clobbers on all paths to entry. */
+ if (fentry_reached
+ /* ??? We'd like to use ref_may_alias_global_p but that
+ excludes global readonly memory and thus we get bougs
+ warnings from p = cond ? "a" : "b" for example. */
+ && (!VAR_P (base)
+ || is_global_var (base)))
+ continue;
/* We didn't find any may-defs so on all paths either
reached function entry or a killing clobber. */
bool found_cd_chain = false;
size_t cur_chain_len = 0;
- if (EDGE_COUNT (bb->succs) < 2)
- return false;
-
- if (*num_calls > PARAM_VALUE (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS))
+ if (*num_calls > param_uninit_control_dep_attempts)
return false;
++*num_calls;
e = one_cd_chain[j];
guard_bb = e->src;
gsi = gsi_last_bb (guard_bb);
+ /* Ignore empty forwarder blocks. */
+ if (empty_block_p (guard_bb) && single_succ_p (guard_bb))
+ continue;
+ /* An empty basic block here is likely a PHI, and is not one
+ of the cases we handle below. */
if (gsi_end_p (gsi))
{
has_valid_pred = false;
for (idx = 0; idx < gimple_switch_num_labels (gs); ++idx)
{
tree tl = gimple_switch_label (gs, idx);
- if (e->dest == label_to_block (CASE_LABEL (tl)))
+ if (e->dest == label_to_block (cfun, CASE_LABEL (tl)))
{
if (!l)
l = tl;
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "\n[CHECK] Found def edge %d in ", (int) i);
- print_gimple_stmt (dump_file, phi, 0, 0);
+ print_gimple_stmt (dump_file, phi, 0);
}
edges->safe_push (opnd_edge);
}
{
fprintf (dump_file, "\n[CHECK] Found def edge %d in ",
(int) i);
- print_gimple_stmt (dump_file, phi, 0, 0);
+ print_gimple_stmt (dump_file, phi, 0);
}
edges->safe_push (opnd_edge);
}
return has_valid_pred;
}
+/* Dump a pred_info. */
+
+static void
+dump_pred_info (pred_info one_pred)
+{
+ if (one_pred.invert)
+ fprintf (dump_file, " (.NOT.) ");
+ print_generic_expr (dump_file, one_pred.pred_lhs);
+ fprintf (dump_file, " %s ", op_symbol_code (one_pred.cond_code));
+ print_generic_expr (dump_file, one_pred.pred_rhs);
+}
+
+/* Dump a pred_chain. */
+
+static void
+dump_pred_chain (pred_chain one_pred_chain)
+{
+ size_t np = one_pred_chain.length ();
+ for (size_t j = 0; j < np; j++)
+ {
+ dump_pred_info (one_pred_chain[j]);
+ if (j < np - 1)
+ fprintf (dump_file, " (.AND.) ");
+ else
+ fprintf (dump_file, "\n");
+ }
+}
+
/* Dumps the predicates (PREDS) for USESTMT. */
static void
dump_predicates (gimple *usestmt, pred_chain_union preds, const char *msg)
{
- size_t i, j;
- pred_chain one_pred_chain = vNULL;
fprintf (dump_file, "%s", msg);
- print_gimple_stmt (dump_file, usestmt, 0, 0);
- fprintf (dump_file, "is guarded by :\n\n");
+ if (usestmt)
+ {
+ print_gimple_stmt (dump_file, usestmt, 0);
+ fprintf (dump_file, "is guarded by :\n\n");
+ }
size_t num_preds = preds.length ();
- /* Do some dumping here: */
- for (i = 0; i < num_preds; i++)
+ for (size_t i = 0; i < num_preds; i++)
{
- size_t np;
-
- one_pred_chain = preds[i];
- np = one_pred_chain.length ();
-
- for (j = 0; j < np; j++)
- {
- pred_info one_pred = one_pred_chain[j];
- if (one_pred.invert)
- fprintf (dump_file, " (.NOT.) ");
- print_generic_expr (dump_file, one_pred.pred_lhs, 0);
- fprintf (dump_file, " %s ", op_symbol_code (one_pred.cond_code));
- print_generic_expr (dump_file, one_pred.pred_rhs, 0);
- if (j < np - 1)
- fprintf (dump_file, " (.AND.) ");
- else
- fprintf (dump_file, "\n");
- }
+ dump_pred_chain (preds[i]);
if (i < num_preds - 1)
fprintf (dump_file, "(.OR.)\n");
else
return tc;
}
-/* Returns true if VAL falls in the range defined by BOUNDARY and CMPC, i.e.
- all values in the range satisfies (x CMPC BOUNDARY) == true. */
+/* Returns whether VAL CMPC BOUNDARY is true. */
static bool
is_value_included_in (tree val, tree boundary, enum tree_code cmpc)
{
bool inverted = false;
- bool is_unsigned;
bool result;
/* Only handle integer constant here. */
if (TREE_CODE (val) != INTEGER_CST || TREE_CODE (boundary) != INTEGER_CST)
return true;
- is_unsigned = TYPE_UNSIGNED (TREE_TYPE (val));
-
if (cmpc == GE_EXPR || cmpc == GT_EXPR || cmpc == NE_EXPR)
{
cmpc = invert_tree_comparison (cmpc, false);
inverted = true;
}
- if (is_unsigned)
- {
- 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 (cmpc == EQ_EXPR)
+ result = tree_int_cst_equal (val, boundary);
+ else if (cmpc == LT_EXPR)
+ result = tree_int_cst_lt (val, boundary);
else
{
- 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_equal (val, boundary)
- || tree_int_cst_lt (val, boundary));
- }
+ gcc_assert (cmpc == LE_EXPR);
+ result = tree_int_cst_le (val, boundary);
}
if (inverted)
return result;
}
+/* Returns whether 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. It modifies the
+ question from whether VAL & BOUNDARY != 0 to whether VAL & BOUNDARY == VAL.
+ For other values of CMPC, EXACT_P is ignored. */
+
+static bool
+value_sat_pred_p (tree val, tree boundary, enum 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);
+ else
+ return andw.to_uhwi ();
+}
+
/* Returns true if PRED is common among all the predicate
chains (PREDS) (and therefore can be factored out).
NUM_PRED_CHAIN is the size of array PREDS. */
/* Returns true of the domain of single predicate expression
EXPR1 is a subset of that of EXPR2. Returns false if it
- can not be proved. */
+ cannot be proved. */
static bool
is_pred_expr_subset_of (pred_info expr1, pred_info expr2)
if (expr2.invert)
code2 = invert_tree_comparison (code2, false);
- if ((code1 == EQ_EXPR || code1 == BIT_AND_EXPR) && code2 == BIT_AND_EXPR)
- return wi::eq_p (expr1.pred_rhs,
- wi::bit_and (expr1.pred_rhs, expr2.pred_rhs));
-
- if (code1 != code2 && code2 != NE_EXPR)
+ if (code2 == NE_EXPR && code1 == NE_EXPR)
return false;
- if (is_value_included_in (expr1.pred_rhs, expr2.pred_rhs, code2))
- return true;
+ if (code2 == NE_EXPR)
+ return !value_sat_pred_p (expr2.pred_rhs, expr1.pred_rhs, code1);
+
+ if (code1 == EQ_EXPR)
+ return value_sat_pred_p (expr1.pred_rhs, expr2.pred_rhs, code2);
+
+ if (code1 == code2)
+ return value_sat_pred_p (expr1.pred_rhs, expr2.pred_rhs, code2,
+ code1 == BIT_AND_EXPR);
return false;
}
/* Returns true if the domain of PRED1 is a subset
- of that of PRED2. Returns false if it can not be proved so. */
+ of that of PRED2. Returns false if it cannot be proved so. */
static bool
is_pred_chain_subset_of (pred_chain pred1, pred_chain pred2)
individual predicate chains (won't be a compile time problem
as the chains are pretty short). When the function returns
false, it does not necessarily mean *PREDS1 is not a superset
- of *PREDS2, but mean it may not be so since the analysis can
- not prove it. In such cases, false warnings may still be
+ of *PREDS2, but mean it may not be so since the analysis cannot
+ prove it. In such cases, false warnings may still be
emitted. */
static bool
return true;
}
-/* Returns true if TC is AND or OR. */
-
-static inline bool
-is_and_or_or_p (enum tree_code tc, tree type)
-{
- return (tc == BIT_IOR_EXPR
- || (tc == BIT_AND_EXPR
- && (type == 0 || TREE_CODE (type) == BOOLEAN_TYPE)));
-}
-
/* Returns true if X1 is the negate of X2. */
static inline bool
}
/* Return TRUE if PREDICATE can be invalidated by any individual
- predicate in WORKLIST. */
+ predicate in USE_GUARD. */
static bool
can_one_predicate_be_invalidated_p (pred_info predicate,
pred_chain use_guard)
{
+ if (dump_file && dump_flags & TDF_DETAILS)
+ {
+ fprintf (dump_file, "Testing if this predicate: ");
+ dump_pred_info (predicate);
+ fprintf (dump_file, "\n...can be invalidated by a USE guard of: ");
+ dump_pred_chain (use_guard);
+ }
for (size_t i = 0; i < use_guard.length (); ++i)
{
/* NOTE: This is a very simple check, and only understands an
invalidate with say [i > 5] or [i == 8]. There is certainly
room for improvement here. */
if (pred_neg_p (predicate, use_guard[i]))
- return true;
+ {
+ if (dump_file && dump_flags & TDF_DETAILS)
+ {
+ fprintf (dump_file, " Predicate was invalidated by: ");
+ dump_pred_info (use_guard[i]);
+ fputc ('\n', dump_file);
+ }
+ return true;
+ }
}
return false;
}
{
if (uninit_pred.is_empty ())
return false;
+ if (dump_file && dump_flags & TDF_DETAILS)
+ dump_predicates (NULL, uninit_pred,
+ "Testing if anything here can be invalidated: ");
for (size_t i = 0; i < uninit_pred.length (); ++i)
{
pred_chain c = uninit_pred[i];
- for (size_t j = 0; j < c.length (); ++j)
- if (!can_one_predicate_be_invalidated_p (c[j], use_guard))
- return false;
+ size_t j;
+ for (j = 0; j < c.length (); ++j)
+ if (can_one_predicate_be_invalidated_p (c[j], use_guard))
+ break;
+
+ /* If we were unable to invalidate any predicate in C, then there
+ is a viable path from entry to the PHI where the PHI takes
+ an uninitialized value and continues to a use of the PHI. */
+ if (j == c.length ())
+ return false;
}
return true;
}
/* Build the control dependency chain for uninit operand `i'... */
uninit_preds = vNULL;
- if (!compute_control_dep_chain (find_dom (e->src),
+ if (!compute_control_dep_chain (ENTRY_BLOCK_PTR_FOR_FN (cfun),
e->src, dep_chains, &num_chains,
&cur_chain, &num_calls))
{
break;
}
/* ...and convert it into a set of predicates. */
- convert_control_dep_chain_into_preds (dep_chains, num_chains,
- &uninit_preds);
+ bool has_valid_preds
+ = convert_control_dep_chain_into_preds (dep_chains, num_chains,
+ &uninit_preds);
for (size_t j = 0; j < num_chains; ++j)
dep_chains[j].release ();
+ if (!has_valid_preds)
+ {
+ ret = false;
+ break;
+ }
simplify_preds (&uninit_preds, NULL, false);
uninit_preds = normalize_preds (uninit_preds, NULL, false);
USE_STMT is guarded with a predicate set not overlapping with
predicate sets of all runtime paths that do not have a definition.
- Returns false if it is not or it can not be determined. USE_BB is
+ Returns false if it is not or 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 src bb of the operand edge).
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "[CHECK]: Found unguarded use: ");
- print_gimple_stmt (dump_file, use_stmt, 0, 0);
+ print_gimple_stmt (dump_file, use_stmt, 0);
}
/* Found one real use, return. */
if (gimple_code (use_stmt) != GIMPLE_PHI)
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "[WORKLIST]: Update worklist with phi: ");
- print_gimple_stmt (dump_file, use_stmt, 0, 0);
+ print_gimple_stmt (dump_file, use_stmt, 0);
}
worklist->safe_push (as_a<gphi *> (use_stmt));
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "[CHECK]: examining phi: ");
- print_gimple_stmt (dump_file, phi, 0, 0);
+ print_gimple_stmt (dump_file, phi, 0);
}
/* Now check if we have any use of the value without proper guard. */
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "[WORKLIST]: add to initial list: ");
- print_gimple_stmt (dump_file, phi, 0, 0);
+ print_gimple_stmt (dump_file, phi, 0);
}
break;
}
warn_uninitialized_vars (/*warn_possibly_uninitialized=*/!optimize);
- /* Post-dominator information can not be reliably updated. Free it
+ /* Post-dominator information cannot be reliably updated. Free it
after the use. */
free_dominance_info (CDI_POST_DOMINATORS);