From a2602ab7fca96e382d5a519150172a358004e9c9 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 17 Jun 2020 18:17:02 -0400 Subject: [PATCH 233/315] FIXME: make eval_constraint const --- gcc/analyzer/constraint-manager2.cc | 322 +++++++++++++++--- gcc/analyzer/constraint-manager2.h | 47 ++- .../gcc.dg/analyzer/conditionals-notrans.c | 21 +- gcc/testsuite/gcc.dg/analyzer/loop.c | 7 +- 4 files changed, 324 insertions(+), 73 deletions(-) diff --git a/gcc/analyzer/constraint-manager2.cc b/gcc/analyzer/constraint-manager2.cc index bdc4d9819b5..3ad56fc277c 100644 --- a/gcc/analyzer/constraint-manager2.cc +++ b/gcc/analyzer/constraint-manager2.cc @@ -54,38 +54,17 @@ along with GCC; see the file COPYING3. If not see namespace ana { -/* One of the end-points of a range2. */ - -struct bound2 -{ - bound2 () : m_constant (NULL_TREE), m_closed (false) {} - bound2 (tree constant, bool closed) - : m_constant (constant), m_closed (closed) {} - - void ensure_closed (bool is_upper); - - const char * get_relation_as_str () const; - - tree m_constant; - bool m_closed; -}; - -/* A range of values, used for determining if a value has been - constrained to just one possible constant value. */ - -struct range2 +static tristate +compare_constants (tree lhs_const, enum tree_code op, tree rhs_const) { - range2 () : m_lower_bound (), m_upper_bound () {} - range2 (const bound2 &lower, const bound2 &upper) - : m_lower_bound (lower), m_upper_bound (upper) {} - - void dump (pretty_printer *pp) const; - - bool constrained_to_single_element (tree *out); - - bound2 m_lower_bound; - bound2 m_upper_bound; -}; + tree comparison + = fold_binary (op, boolean_type_node, lhs_const, rhs_const); + if (comparison == boolean_true_node) + return tristate (tristate::TS_TRUE); + if (comparison == boolean_false_node) + return tristate (tristate::TS_FALSE); + return tristate (tristate::TS_UNKNOWN); +} /* struct bound2. */ @@ -125,13 +104,44 @@ bound2::get_relation_as_str () const /* Dump this range2 to PP, which must support %E for tree. */ void -range2::dump (pretty_printer *pp) const +range2::dump_to_pp (pretty_printer *pp) const +{ + if (m_lower_bound.m_constant) + { + if (m_upper_bound.m_constant) + pp_printf (pp, "%qE %s x %s %qE", + m_lower_bound.m_constant, + m_lower_bound.get_relation_as_str (), + m_upper_bound.get_relation_as_str (), + m_upper_bound.m_constant); + else + pp_printf (pp, "%qE %s x", + m_lower_bound.m_constant, + m_lower_bound.get_relation_as_str ()); + } + else + { + if (m_upper_bound.m_constant) + pp_printf (pp, "x %s %qE", + m_upper_bound.get_relation_as_str (), + m_upper_bound.m_constant); + else + pp_string (pp, "x"); + } +} + +/* Dump this range2 to stderr. */ + +DEBUG_FUNCTION void +range2::dump () const { - pp_printf (pp, "%qE %s x %s %qE", - m_lower_bound.m_constant, - m_lower_bound.get_relation_as_str (), - m_upper_bound.get_relation_as_str (), - m_upper_bound.m_constant); + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = stderr; + dump_to_pp (&pp); + pp_newline (&pp); + pp_flush (&pp); } /* Determine if there is only one possible value for this range2. @@ -141,6 +151,10 @@ range2::dump (pretty_printer *pp) const bool range2::constrained_to_single_element (tree *out) { + if (m_lower_bound.m_constant == NULL_TREE + || m_upper_bound.m_constant == NULL_TREE) + return false; + if (!INTEGRAL_TYPE_P (TREE_TYPE (m_lower_bound.m_constant))) return false; if (!INTEGRAL_TYPE_P (TREE_TYPE (m_upper_bound.m_constant))) @@ -163,6 +177,95 @@ range2::constrained_to_single_element (tree *out) return false; } +/* Eval the condition "X OP RHS_CONST" for X within the range. */ + +tristate +range2::eval_condition (enum tree_code op, + tree rhs_const) const +{ +#if 0 + range2 copy (*this); + tree single_element; + if (copy.constrained_to_single_element (&single_element)) + return compare_constants (single_element, op, rhs_const); +#endif + + switch (op) + { + case EQ_EXPR: + if (below_lower_bound (rhs_const)) + return tristate (tristate::TS_FALSE); + if (above_upper_bound (rhs_const)) + return tristate (tristate::TS_FALSE); + break; + + case LT_EXPR: + // TODO: distinction between LT and GE + case LE_EXPR: + /* Qn: "X < RHS_CONST". */ + /* If RHS_CONST > upper bound, then it's true. + If RHS_CONST < lower bound, then it's false. + Otherwise unknown. */ + if (above_upper_bound (rhs_const)) + return tristate (tristate::TS_TRUE); + if (below_lower_bound (rhs_const)) + return tristate (tristate::TS_FALSE); + break; + + case NE_EXPR: + /* Qn: "X != RHS_CONST". */ + /* If RHS_CONST < lower bound, then it's true. + If RHS_CONST > upper bound, then it's false. + Otherwise unknown. */ + if (below_lower_bound (rhs_const)) + return tristate (tristate::TS_TRUE); + if (above_upper_bound (rhs_const)) + return tristate (tristate::TS_TRUE); + break; + + case GE_EXPR: + // TODO: distinction between GE and GT + case GT_EXPR: + /* Qn: "X > RHS_CONST". */ + if (above_upper_bound (rhs_const)) + return tristate (tristate::TS_FALSE); + if (below_lower_bound (rhs_const)) + return tristate (tristate::TS_TRUE); + break; + + default: + gcc_unreachable (); // TODO + break; + } + return tristate (tristate::TS_UNKNOWN); +} + +/* Return true if RHS_CONST is below the lower bound of this range. */ + +bool +range2::below_lower_bound (tree rhs_const) const +{ + if (!m_lower_bound.m_constant) + return false; + + return compare_constants (rhs_const, + m_lower_bound.m_closed ? LT_EXPR : LE_EXPR, + m_lower_bound.m_constant).is_true (); +} + +/* Return true if RHS_CONST is above the upper bound of this range. */ + +bool +range2::above_upper_bound (tree rhs_const) const +{ + if (!m_upper_bound.m_constant) + return false; + + return compare_constants (rhs_const, + m_upper_bound.m_closed ? GT_EXPR : GE_EXPR, + m_upper_bound.m_constant).is_true (); +} + /* class equiv_class2. */ /* equiv_class2's default ctor. */ @@ -1027,7 +1130,7 @@ constraint_manager2::get_or_add_equiv_class2 (const svalue2 *sval) tristate constraint_manager2::eval_condition (equiv_class2_id lhs_ec, enum tree_code op, - equiv_class2_id rhs_ec) + equiv_class2_id rhs_ec) const { if (lhs_ec == rhs_ec) { @@ -1051,12 +1154,10 @@ constraint_manager2::eval_condition (equiv_class2_id lhs_ec, tree rhs_const = rhs_ec.get_obj (*this).get_any_constant (); if (lhs_const && rhs_const) { - tree comparison - = fold_binary (op, boolean_type_node, lhs_const, rhs_const); - if (comparison == boolean_true_node) - return tristate (tristate::TS_TRUE); - if (comparison == boolean_false_node) - return tristate (tristate::TS_FALSE); + tristate result_for_constants + = compare_constants (lhs_const, op, rhs_const); + if (result_for_constants.is_known ()) + return result_for_constants; } enum tree_code swapped_op = swap_tree_comparison (op); @@ -1087,13 +1188,92 @@ constraint_manager2::eval_condition (equiv_class2_id lhs_ec, return tristate (tristate::TS_UNKNOWN); } -/* Evaluate the condition LHS OP RHS, creating equiv_class2 instances for - LHS and RHS if they aren't already in equiv_class2es. */ +range2 +constraint_manager2::get_ec_bounds (equiv_class2_id ec_id) const +{ + range2 result; + + int i; + constraint2 *c; + FOR_EACH_VEC_ELT (m_constraints, i, c) + { + if (c->m_lhs == ec_id) + { + if (tree other_cst = c->m_rhs.get_obj (*this).get_any_constant ()) + switch (c->m_op) + { + default: + gcc_unreachable (); + case CONSTRAINT2_NE: + continue; + + case CONSTRAINT2_LT: + /* We have "EC_ID < OTHER_CST". */ + result.m_upper_bound = bound2 (other_cst, false); + break; + + case CONSTRAINT2_LE: + /* We have "EC_ID <= OTHER_CST". */ + result.m_upper_bound = bound2 (other_cst, true); + break; + } + } + if (c->m_rhs == ec_id) + { + if (tree other_cst = c->m_lhs.get_obj (*this).get_any_constant ()) + switch (c->m_op) + { + default: + gcc_unreachable (); + case CONSTRAINT2_NE: + continue; + + case CONSTRAINT2_LT: + /* We have "OTHER_CST < EC_ID" + i.e. "EC_ID >= OTHER_CST". */ + result.m_lower_bound = bound2 (other_cst, true); + break; + + case CONSTRAINT2_LE: + /* We have "OTHER_CST <= EC_ID" + i.e. "EC_ID > OTHER_CST". */ + result.m_lower_bound = bound2 (other_cst, false); + break; + } + } + } + + return result; +} + + +/* Evaluate the condition LHS_EC OP RHS_CONST, avoiding the creation + of equiv_class2 instances. */ + +tristate +constraint_manager2::eval_condition (equiv_class2_id lhs_ec, + enum tree_code op, + tree rhs_const) const +{ + gcc_assert (!lhs_ec.null_p ()); + gcc_assert (CONSTANT_CLASS_P (rhs_const)); + + if (tree lhs_const = lhs_ec.get_obj (*this).get_any_constant ()) + return compare_constants (lhs_const, op, rhs_const); + + /* Look at existing bounds on LHS_EC. */ + range2 lhs_bounds = get_ec_bounds (lhs_ec); + return lhs_bounds.eval_condition (op, rhs_const); +} + +// FIXME: +/* Evaluate the condition LHS OP RHS, avoiding the creation + of equiv_class2 instances. */ tristate constraint_manager2::eval_condition (const svalue2 *lhs, enum tree_code op, - const svalue2 *rhs) + const svalue2 *rhs) const { lhs = lhs->unwrap_any_unmergeable (); rhs = rhs->unwrap_any_unmergeable (); @@ -1103,9 +1283,45 @@ constraint_manager2::eval_condition (const svalue2 *lhs, || rhs->get_kind () == svalue2::SK_UNKNOWN) return tristate (tristate::TS_UNKNOWN); - return eval_condition (get_or_add_equiv_class2 (lhs), - op, - get_or_add_equiv_class2 (rhs)); + equiv_class2_id lhs_ec (-1); + equiv_class2_id rhs_ec (-1); + get_equiv_class2_by_svalue2 (lhs, &lhs_ec); + get_equiv_class2_by_svalue2 (rhs, &rhs_ec); + if (!lhs_ec.null_p () && !rhs_ec.null_p ()) + return eval_condition (lhs_ec, op, rhs_ec); + + /* If at least one is not in an EC, we have no constraints + comparing LHS and RHS yet. + They might still be comparable if one (or both) is a constant. */ + tree lhs_const = lhs->maybe_get_constant (); + tree rhs_const = rhs->maybe_get_constant (); +#if 0 + if (lhs_const && rhs_const) + { + tristate result_for_constants + = compare_constants (lhs_const, op, rhs_const); + if (result_for_constants.is_known ()) + return result_for_constants; + } +#endif + + if (!lhs_ec.null_p ()) + { + gcc_assert (rhs_ec.null_p ()); + if (rhs_const) + return eval_condition (lhs_ec, op, rhs_const); + } + if (!rhs_ec.null_p ()) + { + gcc_assert (lhs_ec.null_p ()); + if (lhs_const) + { + enum tree_code swapped_op = swap_tree_comparison (op); + return eval_condition (rhs_ec, swapped_op, lhs_const); + } + } + + return tristate (tristate::TS_UNKNOWN); } /* Delete any information about svalue2_id instances identified by P. @@ -2236,13 +2452,15 @@ test_constraint_impl () /* Assert various things about the insides of model. */ constraint_manager2 *cm = model.get_constraints2 (); - ASSERT_EQ (cm->m_constraints.length (), 1); - ASSERT_EQ (cm->m_equiv_class2es.length (), 2); + ASSERT_EQ (cm->m_constraints.length (), 0); + ASSERT_EQ (cm->m_equiv_class2es.length (), 1); +#if 0 ASSERT_EQ (cm->m_constraints[0].m_lhs, cm->get_or_add_equiv_class2 (model.get_rvalue (int_0, NULL))); ASSERT_EQ (cm->m_constraints[0].m_rhs, cm->get_or_add_equiv_class2 (model.get_rvalue (int_42, NULL))); ASSERT_EQ (cm->m_constraints[0].m_op, CONSTRAINT2_LT); +#endif } // TODO: selftest for merging ecs "in the middle" diff --git a/gcc/analyzer/constraint-manager2.h b/gcc/analyzer/constraint-manager2.h index 5425de08ab9..13c2b5d3cc5 100644 --- a/gcc/analyzer/constraint-manager2.h +++ b/gcc/analyzer/constraint-manager2.h @@ -25,6 +25,45 @@ namespace ana { class constraint_manager2; +/* One of the end-points of a range2. */ + +struct bound2 +{ + bound2 () : m_constant (NULL_TREE), m_closed (false) {} + bound2 (tree constant, bool closed) + : m_constant (constant), m_closed (closed) {} + + void ensure_closed (bool is_upper); + + const char * get_relation_as_str () const; + + tree m_constant; + bool m_closed; +}; + +/* A range of values, used for determining if a value has been + constrained to just one possible constant value. */ + +struct range2 +{ + range2 () : m_lower_bound (), m_upper_bound () {} + range2 (const bound2 &lower, const bound2 &upper) + : m_lower_bound (lower), m_upper_bound (upper) {} + + void dump_to_pp (pretty_printer *pp) const; + void dump () const; + + bool constrained_to_single_element (tree *out); + + tristate eval_condition (enum tree_code op, + tree rhs_const) const; + bool below_lower_bound (tree rhs_const) const; + bool above_upper_bound (tree rhs_const) const; + + bound2 m_lower_bound; + bound2 m_upper_bound; +}; + /* Abstract base class for specifying how state should be purged. */ class purge_criteria2 @@ -213,10 +252,14 @@ public: equiv_class2_id get_or_add_equiv_class2 (const svalue2 *sval); tristate eval_condition (equiv_class2_id lhs, enum tree_code op, - equiv_class2_id rhs); + equiv_class2_id rhs) const; + tristate eval_condition (equiv_class2_id lhs_ec, + enum tree_code op, + tree rhs_const) const; tristate eval_condition (const svalue2 *lhs, enum tree_code op, - const svalue2 *rhs); + const svalue2 *rhs) const; + range2 get_ec_bounds (equiv_class2_id ec_id) const; void purge (const purge_criteria2 &p, purge_stats2 *stats); diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c index a00127b1a7f..baee0b684b7 100644 --- a/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c +++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c @@ -7,15 +7,12 @@ void test (int i, int j) { __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */ __analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */ - __analyzer_eval (i > 3); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (i > 3); /* { dg-warning "TRUE" } */ __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */ - __analyzer_eval (i != 3); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (i != 3); /* { dg-warning "TRUE" } */ - __analyzer_eval (i == 3); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */ __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */ __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */ @@ -43,21 +40,17 @@ void test (int i, int j) __analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */ __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */ - __analyzer_eval (i > 5); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (i > 5); /* { dg-warning "FALSE" } */ __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */ - __analyzer_eval (i == 5); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ - __analyzer_eval (i != 5); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */ + __analyzer_eval (i != 5); /* { dg-warning "TRUE" } */ __analyzer_eval (i < 5); /* { dg-warning "TRUE" } */ - __analyzer_eval (i <= 5); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */ } } diff --git a/gcc/testsuite/gcc.dg/analyzer/loop.c b/gcc/testsuite/gcc.dg/analyzer/loop.c index e5f327e2847..c4cfd88c912 100644 --- a/gcc/testsuite/gcc.dg/analyzer/loop.c +++ b/gcc/testsuite/gcc.dg/analyzer/loop.c @@ -1,5 +1,3 @@ -/* { dg-additional-options "-fno-analyzer-state-purge" } */ - #include "analyzer-decls.h" void test(void) @@ -12,13 +10,12 @@ void test(void) __analyzer_eval (i < 256); /* { dg-warning "TRUE" } */ /* (should report TRUE twice). */ - __analyzer_eval (i == 0); /* { dg-warning "TRUE" "1st" } */ + __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */ /* { dg-warning "FALSE" "2nd" { xfail *-*-* } .-1 } */ /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */ - __analyzer_eval (i >= 0); /* { dg-warning "TRUE" "1st" } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ + __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */ } -- 2.26.2