public inbox for gcc-cvs@sourceware.org help / color / mirror / Atom feed
From: David Malcolm <dmalcolm@gcc.gnu.org> To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-5815] analyzer: fix equivalence class state purging [PR103533] Date: Mon, 6 Dec 2021 23:37:13 +0000 (GMT) [thread overview] Message-ID: <20211206233713.EE0373858D28@sourceware.org> (raw) https://gcc.gnu.org/g:c9543403c19fdc3c3b5a8db8546340de085bd14e commit r12-5815-gc9543403c19fdc3c3b5a8db8546340de085bd14e Author: David Malcolm <dmalcolm@redhat.com> Date: Mon Dec 6 14:04:35 2021 -0500 analyzer: fix equivalence class state purging [PR103533] Whilst debugging state explosions seen when enabling taint detection with -fanalyzer (PR analyzer/103533), I noticed that constraint manager instances could contain stray, redundant constants, such as this instance: constraint_manager: equiv classes: ec0: {(int)0 == [m_constant]‘0’} ec1: {(size_t)4 == [m_constant]‘4’} constraints: where there are two equivalence classes, each just containing a constant, with no constraints using them. This patch makes constraint_manager::canonicalize more aggressive about purging state, handling the case of purging a redundant EC containing just a constant. gcc/analyzer/ChangeLog: PR analyzer/103533 * constraint-manager.cc (equiv_class::contains_non_constant_p): New. (constraint_manager::canonicalize): Call it when determining redundant ECs. (selftest::test_purging): New selftest. (selftest::run_constraint_manager_tests): Likewise. * constraint-manager.h (equiv_class::contains_non_constant_p): New decl. Signed-off-by: David Malcolm <dmalcolm@redhat.com> Diff: --- gcc/analyzer/constraint-manager.cc | 149 ++++++++++++++++++++++++++++++++++++- gcc/analyzer/constraint-manager.h | 2 + 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc index ea6b5dc60e0..76e44e77d0a 100644 --- a/gcc/analyzer/constraint-manager.cc +++ b/gcc/analyzer/constraint-manager.cc @@ -1145,6 +1145,30 @@ equiv_class::canonicalize () m_vars.qsort (svalue::cmp_ptr_ptr); } +/* Return true if this EC contains a variable, false if it merely + contains constants. + Subroutine of constraint_manager::canonicalize, for removing + redundant ECs. */ + +bool +equiv_class::contains_non_constant_p () const +{ + if (m_constant) + { + for (auto iter : m_vars) + if (iter->maybe_get_constant ()) + continue; + else + /* We have {non-constant == constant}. */ + return true; + /* We only have constants. */ + return false; + } + else + /* Return true if we have {non-constant == non-constant}. */ + return m_vars.length () > 1; +} + /* Get a debug string for C_OP. */ const char * @@ -2718,8 +2742,7 @@ constraint_manager::canonicalize () { equiv_class *ec = m_equiv_classes[i]; if (!used_ecs.contains (ec) - && ((ec->m_vars.length () < 2 && ec->m_constant == NULL_TREE) - || (ec->m_vars.length () == 0))) + && !ec->contains_non_constant_p ()) { m_equiv_classes.unordered_remove (i); delete ec; @@ -3704,6 +3727,127 @@ test_many_constants () } } +/* Verify that purging state relating to a variable doesn't leave stray + equivalence classes (after canonicalization). */ + +static void +test_purging (void) +{ + tree int_0 = build_int_cst (integer_type_node, 0); + tree a = build_global_decl ("a", integer_type_node); + tree b = build_global_decl ("b", integer_type_node); + + /* "a != 0". */ + { + region_model_manager mgr; + region_model model (&mgr); + ADD_SAT_CONSTRAINT (model, a, NE_EXPR, int_0); + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 2); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 1); + + /* Purge state for "a". */ + const svalue *sval_a = model.get_rvalue (a, NULL); + model.purge_state_involving (sval_a, NULL); + model.canonicalize (); + /* We should have an empty constraint_manager. */ + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 0); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 0); + } + + /* "a != 0" && "b != 0". */ + { + region_model_manager mgr; + region_model model (&mgr); + ADD_SAT_CONSTRAINT (model, a, NE_EXPR, int_0); + ADD_SAT_CONSTRAINT (model, b, NE_EXPR, int_0); + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 3); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 2); + + /* Purge state for "a". */ + const svalue *sval_a = model.get_rvalue (a, NULL); + model.purge_state_involving (sval_a, NULL); + model.canonicalize (); + /* We should just have the constraint/ECs involving b != 0. */ + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 2); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 1); + ASSERT_CONDITION_TRUE (model, b, NE_EXPR, int_0); + } + + /* "a != 0" && "b == 0". */ + { + region_model_manager mgr; + region_model model (&mgr); + ADD_SAT_CONSTRAINT (model, a, NE_EXPR, int_0); + ADD_SAT_CONSTRAINT (model, b, EQ_EXPR, int_0); + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 2); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 1); + + /* Purge state for "a". */ + const svalue *sval_a = model.get_rvalue (a, NULL); + model.purge_state_involving (sval_a, NULL); + model.canonicalize (); + /* We should just have the EC involving b == 0. */ + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 1); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 0); + ASSERT_CONDITION_TRUE (model, b, EQ_EXPR, int_0); + } + + /* "a == 0". */ + { + region_model_manager mgr; + region_model model (&mgr); + ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, int_0); + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 1); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 0); + + /* Purge state for "a". */ + const svalue *sval_a = model.get_rvalue (a, NULL); + model.purge_state_involving (sval_a, NULL); + model.canonicalize (); + /* We should have an empty constraint_manager. */ + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 0); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 0); + } + + /* "a == 0" && "b != 0". */ + { + region_model_manager mgr; + region_model model (&mgr); + ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, int_0); + ADD_SAT_CONSTRAINT (model, b, NE_EXPR, int_0); + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 2); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 1); + + /* Purge state for "a". */ + const svalue *sval_a = model.get_rvalue (a, NULL); + model.purge_state_involving (sval_a, NULL); + model.canonicalize (); + /* We should just have the constraint/ECs involving b != 0. */ + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 2); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 1); + ASSERT_CONDITION_TRUE (model, b, NE_EXPR, int_0); + } + + /* "a == 0" && "b == 0". */ + { + region_model_manager mgr; + region_model model (&mgr); + ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, int_0); + ADD_SAT_CONSTRAINT (model, b, EQ_EXPR, int_0); + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 1); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 0); + + /* Purge state for "a". */ + const svalue *sval_a = model.get_rvalue (a, NULL); + model.purge_state_involving (sval_a, NULL); + model.canonicalize (); + /* We should just have the EC involving b == 0. */ + ASSERT_EQ (model.get_constraints ()->m_equiv_classes.length (), 1); + ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 0); + ASSERT_CONDITION_TRUE (model, b, EQ_EXPR, int_0); + } +} + /* Implementation detail of ASSERT_DUMP_BOUNDED_RANGES_EQ. */ static void @@ -4035,6 +4179,7 @@ run_constraint_manager_tests (bool transitivity) test_constraint_impl (); test_equality (); test_many_constants (); + test_purging (); test_bounded_range (); test_bounded_ranges (); diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h index 0a430eae91f..c7b9d9c697b 100644 --- a/gcc/analyzer/constraint-manager.h +++ b/gcc/analyzer/constraint-manager.h @@ -248,6 +248,8 @@ public: json::object *to_json () const; + bool contains_non_constant_p () const; + /* An equivalence class can contain multiple constants (e.g. multiple different zeroes, for different types); these are just for the last constant added. */
reply other threads:[~2021-12-06 23:37 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20211206233713.EE0373858D28@sourceware.org \ --to=dmalcolm@gcc.gnu.org \ --cc=gcc-cvs@gcc.gnu.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).