From 9882fef92744d39c6825c0d4b652869e3576f083 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Thu, 14 May 2020 15:28:07 -0400 Subject: [PATCH 132/179] FIXME: initial WIP on state merging --- gcc/analyzer/constraint-manager2.cc | 14 +- gcc/analyzer/constraint-manager2.h | 4 +- gcc/analyzer/program-state.cc | 43 ++++- gcc/analyzer/program-state.h | 3 + gcc/analyzer/region-model2.cc | 276 ++++++++++++++-------------- gcc/analyzer/region-model2.h | 19 +- gcc/analyzer/store2.cc | 193 ++++++++++++++++++- gcc/analyzer/store2.h | 20 +- 8 files changed, 408 insertions(+), 164 deletions(-) diff --git a/gcc/analyzer/constraint-manager2.cc b/gcc/analyzer/constraint-manager2.cc index e6a6c21653c..80cc79802f9 100644 --- a/gcc/analyzer/constraint-manager2.cc +++ b/gcc/analyzer/constraint-manager2.cc @@ -1355,18 +1355,22 @@ private: constraint_manager2 *m_out; }; +// FIXME /* Use MERGER to merge CM_A and CM_B into *OUT. If one thinks of a constraint_manager2 as a subset of N-dimensional space, this takes the union of the points of CM_A and CM_B, and expresses that into *OUT. Alternatively, it can be thought of as the intersection of the constraints. */ -#if 0 + void constraint_manager2::merge (const constraint_manager2 &cm_a, - const constraint_manager2 &cm_b, - constraint_manager2 *out, - const model_merger &merger) + const constraint_manager2 &cm_b, + constraint_manager2 *out/*, + const model_merger &merger*/) { + // TODO + // gcc_unreachable (); +#if 0 gcc_assert (merger.m_sval_mapping); /* Map svalue2_ids in each equiv class from both sources @@ -1392,8 +1396,8 @@ constraint_manager2::merge (const constraint_manager2 &cm_a, and add those to *OUT. */ merger_fact_visitor v (&cleaned_cm_b, out); cleaned_cm_a.for_each_fact (&v); -} #endif +} /* A subroutine of constraint_manager2::merge. Use MAP_SVAL_TO_M to map equivalence classes and constraints from diff --git a/gcc/analyzer/constraint-manager2.h b/gcc/analyzer/constraint-manager2.h index 0e855dd3267..7c1fec5db6e 100644 --- a/gcc/analyzer/constraint-manager2.h +++ b/gcc/analyzer/constraint-manager2.h @@ -222,8 +222,8 @@ public: static void merge (const constraint_manager2 &cm_a, const constraint_manager2 &cm_b, - constraint_manager2 *out, - const model_merger &merger); + constraint_manager2 *out/*, + const model_merger &merger*/); void for_each_fact (fact2_visitor *) const; diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index 505a378e212..ece7b7a58f2 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -1809,15 +1809,14 @@ program_state::can_merge_with_p (const program_state &other, { gcc_assert (out); + if (m_region_model2) + return can_merge_with2_p (other, ext_state, out); + /* TODO: initially I had an early reject here if there are sm-differences between the states. However, this was falsely rejecting merger opportunities for states where the only difference was in svalue_id ordering. */ - // FIXME: - if (m_region_model2) - return false; - /* Attempt to merge the region_models. */ svalue_id_merger_mapping sid_mapping (*m_region_model, @@ -1877,14 +1876,46 @@ program_state::can_merge_with_p (const program_state &other, } } - // TODO: m_checker_states2 - impl_region_model_context ctxt (out, NULL, ext_state); out->m_region_model->canonicalize (&ctxt); return true; } +/* Attempt to merge this state with OTHER, both using EXT_STATE. + Write the result to *OUT. + If the states were merged successfully, return true. */ + +bool +program_state::can_merge_with2_p (const program_state &other, + const extrinsic_state &ext_state, + program_state *out) const +{ + gcc_assert (out); + gcc_assert (m_region_model2); + + /* Early reject if there are sm-differences between the states. */ + int i; + sm_state_map *smap; + FOR_EACH_VEC_ELT (out->m_checker_states, i, smap) + if (m_checker_states[i] != other.m_checker_states[i]) + return false; + + /* Attempt to merge the region_models. */ + if (!m_region_model2->can_merge_with_p (*other.m_region_model2, + out->m_region_model2)) + return false; + + /* Copy m_checker_states to OUT. */ + FOR_EACH_VEC_ELT (out->m_checker_states, i, smap) + { + delete smap; + out->m_checker_states[i] = m_checker_states[i]->clone (); + } + + return true; +} + /* Assert that this object is valid. */ void diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index 3cbdd916328..f9e5065756f 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -409,6 +409,9 @@ public: bool can_merge_with_p (const program_state &other, const extrinsic_state &ext_state, program_state *out) const; + bool can_merge_with2_p (const program_state &other, + const extrinsic_state &ext_state, + program_state *out) const; void validate (const extrinsic_state &ext_state) const; diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc index 3344a800b5c..c679ec54947 100644 --- a/gcc/analyzer/region-model2.cc +++ b/gcc/analyzer/region-model2.cc @@ -2066,19 +2066,6 @@ root_region2::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "root_region2()"); } -/* Implementation of region2::validate vfunc for root_region2. */ -#if 0 -void -root_region2::validate (const region_model2 &model) const -{ - region2::validate (model); - m_stack_reg.validate (model); - m_globals_reg.validate (model); - m_code_reg.validate (model); - m_heap_reg.validate (model); -} -#endif - /* Create a new frame_region2 for a call to FUN and push it onto the stack. @@ -2635,6 +2622,24 @@ region_model2_manager::get_or_create_constant_svalue2 (tree cst_expr) // TODO: cst equality vs tree pointer equality? } +/* Return an svalue2 *for a unknown_svalue2 for CST_EXPR, + creating the unknown_svalue2 if necessary. + The unknown_svalue2 instances are reused, based on pointer equality + of trees */ + +const svalue2 * +region_model2_manager::get_or_create_unknown_svalue2 (tree type) +{ + gcc_assert (type); + + unknown_svalue2 **slot = m_unknowns_map.get (type); + if (slot) + return *slot; + unknown_svalue2 *sval = new unknown_svalue2 (type); + m_unknowns_map.put (type, sval); + return sval; +} + const svalue2 * region_model2_manager::get_or_create_initial_value (const region2 *reg) { @@ -6277,23 +6282,6 @@ region_model2::apply_constraints_for_gswitch (const switch_cfg_superedge &edge, } } -/* Get the root_region2 within this model (guaranteed to be non-null). */ -#if 0 -root_region2 * -region_model2::get_root_region2 () const -{ - return get_region2 (m_root_reg); -} - -/* Get the region2 *of this model's stack region2 (if any). */ - -region2_id -region_model2::get_stack_region2 *() const -{ - return get_root_region2 ()->get_stack_region2 *(); -} -#endif - /* For use with push_frame when handling a top-level call within the analysis. PARAM has a defined but unknown initial value. Anything it points to has escaped, since the calling context "knows" @@ -6802,59 +6790,32 @@ region_model2::poison_any_pointers_to_bad_region2s (const region2_id_set & } #endif /* Attempt to merge THIS with OTHER_MODEL, writing the result - to OUT_MODEL, and populating SID_MAPPING. */ -#if 0 + to OUT_MODEL. */ + bool region_model2::can_merge_with_p (const region_model2 &other_model, - region_model2 *out_model, - svalue2_id_merger_mapping *sid_mapping) const + region_model2 *out_model) const { - gcc_assert (m_root_reg == other_model.m_root_reg); - gcc_assert (m_root_reg.as_int () == 0); - gcc_assert (sid_mapping); gcc_assert (out_model); + gcc_assert (m_mgr == other_model.m_mgr); + gcc_assert (m_mgr == out_model->m_mgr); - model_merger merger (this, &other_model, out_model, sid_mapping); + if (m_current_frame != other_model.m_current_frame) + return false; + out_model->m_current_frame = m_current_frame; - if (!root_region2::can_merge_p (get_root_region2 (), - other_model.get_root_region2 (), - out_model->get_root_region2 (), - &merger)) + if (!store2::can_merge_p (&m_store, &other_model.m_store, + &out_model->m_store)) return false; /* Merge constraints. */ - constraint_manager::merge (*m_constraints, - *other_model.m_constraints, - out_model->m_constraints, - merger); - - out_model->validate (); - - /* The merged model should be simpler (or as simple) as the inputs. */ -#if 0 - gcc_assert (out_model->m_svalue2s.length () <= m_svalue2s.length ()); - gcc_assert (out_model->m_svalue2s.length () - <= other_model.m_svalue2s.length ()); -#endif - gcc_assert (out_model->m_region2s.length () <= m_region2s.length ()); - gcc_assert (out_model->m_region2s.length () - <= other_model.m_region2s.length ()); - // TODO: same, for constraints + constraint_manager2::merge (*m_constraints2, + *other_model.m_constraints2, + out_model->m_constraints2); return true; } -/* As above, but supply a placeholder svalue2_id_merger_mapping - instance to be used and receive output. For use in selftests. */ - -bool -region_model2::can_merge_with_p (const region_model2 &other_model, - region_model2 *out_model) const -{ - svalue2_id_merger_mapping sid_mapping (*this, other_model); - return can_merge_with_p (other_model, out_model, &sid_mapping); -} -#endif /* For debugging purposes: look for a region2 within this region_model2 for a decl named NAME (or an SSA_NAME for such a decl), returning its value, or svalue2_id::null if none are found. */ @@ -8588,6 +8549,7 @@ test_model_equality_4 () model.canonicalize (NULL); } +#endif /* Assert that if we have two region_model2 instances with values VAL_A and VAL_B for EXPR that they are @@ -8598,12 +8560,13 @@ test_model_equality_4 () static void assert_region_model2s_merge (tree expr, tree val_a, tree val_b, - region_model2 *out_merged_model, - const svalue2 **out_merged_svalue2) + region_model2 *out_merged_model, + const svalue2 **out_merged_svalue2) { test_region_model2_context ctxt; - region_model2 model0; - region_model2 model1; + region_model2_manager *mgr = out_merged_model->get_manager (); + region_model2 model0 (mgr); + region_model2 model1 (mgr); if (val_a) model0.set_value (model0.get_lvalue (expr, &ctxt), model0.get_rvalue (val_a, &ctxt), @@ -8615,9 +8578,7 @@ assert_region_model2s_merge (tree expr, tree val_a, tree val_b, /* They should be mergeable. */ ASSERT_TRUE (model0.can_merge_with_p (model1, out_merged_model)); - - const svalue2 *merged_svalue2_sval = out_merged_model->get_rvalue (expr, &ctxt); - *out_merged_svalue2 = out_merged_model->get_svalue2 (merged_svalue2_sval); + *out_merged_svalue2 = out_merged_model->get_rvalue (expr, &ctxt); } /* Verify that we can merge region_model2 instances. */ @@ -8650,10 +8611,12 @@ test_state_merging () get_identifier ("q"), ptr_type_node); + region_model2_manager mgr; + { - region_model2 model0; - region_model2 model1; - region_model2 merged; + region_model2 model0 (&mgr); + region_model2 model1 (&mgr); + region_model2 merged (&mgr); /* Verify empty models can be merged. */ ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); ASSERT_EQ (model0, merged); @@ -8664,9 +8627,9 @@ test_state_merging () /* TODO: verify that the merged model doesn't have a value for the global */ { - region_model2 model0; - region_model2 model1; - region_model2 merged; + region_model2 model0 (&mgr); + region_model2 model1 (&mgr); + region_model2 merged (&mgr); test_region_model2_context ctxt; model0.add_constraint (x, EQ_EXPR, int_42, &ctxt); model1.add_constraint (x, EQ_EXPR, int_113, &ctxt); @@ -8675,16 +8638,20 @@ test_state_merging () ASSERT_NE (model1, merged); } + // FIXME: uses "set_to_new_unknown_value" +#if 0 /* Verify handling of a PARM_DECL. */ { test_region_model2_context ctxt; - region_model2 model0; - region_model2 model1; + region_model2 model0 (&mgr); + region_model2 model1 (&mgr); ASSERT_EQ (model0.get_stack_depth (), 0); model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt); ASSERT_EQ (model0.get_stack_depth (), 1); +#if 0 ASSERT_EQ (model0.get_function_at_depth (0), DECL_STRUCT_FUNCTION (test_fndecl)); +#endif model1.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt); const svalue2 *sval_a @@ -8694,23 +8661,29 @@ test_state_merging () integer_type_node, &ctxt); ASSERT_EQ (model0, model1); + // TODO: +#if 0 /* Check that get_value_by_name works for locals. */ ASSERT_EQ (model0.get_value_by_name ("a"), sid_a); +#endif /* They should be mergeable, and the result should be the same. */ - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); ASSERT_EQ (model0, merged); /* In particular, there should be an unknown value for "a". */ - const svalue2 *merged_a_sval = merged.get_svalue2 (merged.get_rvalue (a, &ctxt)); - ASSERT_EQ (merged_a_sval->get_kind (), SK_UNKNOWN); + const svalue2 *merged_a_sval = merged.get_rvalue (a, &ctxt); + ASSERT_EQ (merged_a_sval->get_kind (), svalue2::SK_UNKNOWN); } +#endif + // FIXME: uses "set_to_new_unknown_value" +#if 0 /* Verify handling of a global. */ { test_region_model2_context ctxt; - region_model2 model0; - region_model2 model1; + region_model2 model0 (&mgr); + region_model2 model1 (&mgr); const svalue2 *sval_x = model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt), integer_type_node, &ctxt); @@ -8718,60 +8691,73 @@ test_state_merging () integer_type_node, &ctxt); ASSERT_EQ (model0, model1); + // TODO +#if 0 /* Check that get_value_by_name works for globals. */ ASSERT_EQ (model0.get_value_by_name ("x"), sid_x); +#endif /* They should be mergeable, and the result should be the same. */ - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); ASSERT_EQ (model0, merged); /* In particular, there should be an unknown value for "x". */ - const svalue2 *merged_x_sval = merged.get_svalue2 (merged.get_rvalue (x, &ctxt)); - ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN); + const svalue2 *merged_x_sval = merged.get_rvalue (x, &ctxt); + ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN); } +#endif /* Use global-handling to verify various combinations of values. */ /* Two equal constant values. */ { - region_model2 merged; + region_model2 merged (&mgr); const svalue2 *merged_x_sval; assert_region_model2s_merge (x, int_42, int_42, &merged, &merged_x_sval); /* In particular, there should be a constant value for "x". */ - ASSERT_EQ (merged_x_sval->get_kind (), SK_CONSTANT); + ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_CONSTANT); ASSERT_EQ (merged_x_sval->dyn_cast_constant_svalue2 ()->get_constant (), int_42); } /* Two non-equal constant values. */ { - region_model2 merged; + region_model2 merged (&mgr); const svalue2 *merged_x_sval; + // FIXME: +#if 0 assert_region_model2s_merge (x, int_42, int_113, &merged, &merged_x_sval); /* In particular, there should be an unknown value for "x". */ - ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN); + ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN); +#endif } /* Uninit and constant. */ { - region_model2 merged; + region_model2 merged (&mgr); const svalue2 *merged_x_sval; + // FIXME: +#if 0 assert_region_model2s_merge (x, NULL_TREE, int_113, &merged, &merged_x_sval); /* In particular, there should be an unknown value for "x". */ - ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN); + ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN); +#endif } /* Constant and uninit. */ { - region_model2 merged; + region_model2 merged (&mgr); const svalue2 *merged_x_sval; + // FIXME: +#if 0 assert_region_model2s_merge (x, int_42, NULL_TREE, &merged, &merged_x_sval); /* In particular, there should be an unknown value for "x". */ - ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN); + ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN); +#endif } /* Unknown and constant. */ @@ -8783,9 +8769,11 @@ test_state_merging () /* Pointers: NULL and non-NULL. */ // TODO + // FIXME: uses "set_to_new_unknown_value" +#if 0 /* Pointers: non-NULL and non-NULL: ptr to a local. */ { - region_model2 model0; + region_model2 model0 (&mgr); model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); model0.set_to_new_unknown_value (model0.get_lvalue (a, NULL), integer_type_node, NULL); @@ -8796,76 +8784,80 @@ test_state_merging () ASSERT_EQ (model0, model1); /* They should be mergeable, and the result should be the same. */ - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); ASSERT_EQ (model0, merged); } +#endif /* Pointers: non-NULL and non-NULL: ptr to a global. */ { - region_model2 merged; + region_model2 merged (&mgr); /* p == &y in both input models. */ const svalue2 *merged_p_sval; assert_region_model2s_merge (p, addr_of_y, addr_of_y, &merged, &merged_p_sval); /* We should get p == &y in the merged model. */ - ASSERT_EQ (merged_p_sval->get_kind (), SK_REGION2); - region_const svalue2 *merged_p_ptr = merged_p_sval->dyn_cast_region_svalue2 (); + ASSERT_EQ (merged_p_sval->get_kind (), svalue2::SK_REGION); + const region_svalue2 *merged_p_ptr + = merged_p_sval->dyn_cast_region_svalue2 (); const region2 *merged_p_star_reg = merged_p_ptr->get_pointee (); ASSERT_EQ (merged_p_star_reg, merged.get_lvalue (y, NULL)); } /* Pointers: non-NULL ptrs to different globals: should be unknown. */ { - region_model2 merged; + region_model2 merged (&mgr); /* x == &y vs x == &z in the input models. */ const svalue2 *merged_x_sval; + // TODO: +#if 0 assert_region_model2s_merge (x, addr_of_y, addr_of_z, &merged, &merged_x_sval); /* We should get x == unknown in the merged model. */ - ASSERT_EQ (merged_x_sval->get_kind (), SK_UNKNOWN); + ASSERT_EQ (merged_x_sval->get_kind (), svalue2::SK_UNKNOWN); +#endif } /* Pointers: non-NULL and non-NULL: ptr to a heap region2. */ { test_region_model2_context ctxt; - region_model2 model0; - const region2 *new_reg = model0.add_new_malloc_region2 (); - const svalue2 *ptr_sval - = model0.get_or_create_ptr_svalue2 (ptr_type_node, new_reg); + region_model2 model0 (&mgr); + tree size = build_int_cst (integer_type_node, 1024); + const svalue2 *size_sval = mgr.get_or_create_constant_svalue2 (size); + const region2 *new_reg = model0.create_region_for_heap_alloc (size_sval); + const svalue2 *ptr_sval = mgr.get_ptr_svalue2 (ptr_type_node, new_reg); model0.set_value (model0.get_lvalue (p, &ctxt), ptr_sval, &ctxt); - model0.canonicalize (&ctxt); region_model2 model1 (model0); ASSERT_EQ (model0, model1); - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); - merged.canonicalize (&ctxt); - - /* The merged model ought to be identical (after canonicalization, - at least). */ + /* The merged model ought to be identical. */ ASSERT_EQ (model0, merged); } + // FIXME: uses "set_to_new_unknown_value" +#if 0 /* Two region2s sharing the same unknown svalue2 should continue sharing an unknown svalue2 after self-merger. */ { test_region_model2_context ctxt; - region_model2 model0; + region_model2 model0 (&mgr); const svalue2 *sval = model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt), integer_type_node, &ctxt); - model0.set_value (model0.get_lvalue (y, &ctxt), sid, &ctxt); + model0.set_value (model0.get_lvalue (y, &ctxt), sval, &ctxt); region_model2 model1 (model0); /* They should be mergeable, and the result should be the same. */ - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); ASSERT_EQ (model0, merged); @@ -8873,11 +8865,12 @@ test_state_merging () ASSERT_EQ (merged.eval_condition (x, EQ_EXPR, y, &ctxt), tristate (tristate::TS_TRUE)); } +#endif #if 0 { - region_model2 model0; - region_model2 model1; + region_model2 model0 (&mgr); + region_model2 model1 (&mgr); test_region_model2_context ctxt; model0.add_constraint (x, EQ_EXPR, int_42, &ctxt); model1.add_constraint (x, NE_EXPR, int_42, &ctxt); @@ -8885,8 +8878,8 @@ test_state_merging () } { - region_model2 model0; - region_model2 model1; + region_model2 model0 (&mgr); + region_model2 model1 (&mgr); test_region_model2_context ctxt; model0.add_constraint (x, EQ_EXPR, int_42, &ctxt); model1.add_constraint (x, NE_EXPR, int_42, &ctxt); @@ -8906,8 +8899,9 @@ test_state_merging () /* Views. */ { +#if 0 test_region_model2_context ctxt; - region_model2 model0; + region_model2 model0 (&mgr); const region2 *x_reg = model0.get_lvalue (x, &ctxt); const region2 *x_as_ptr = model0.get_or_create_view (x_reg, ptr_type_node, @@ -8918,14 +8912,16 @@ test_state_merging () ASSERT_EQ (model1, model0); /* They should be mergeable, and the result should be the same. */ - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); +#endif } /* Verify that we can merge a model in which a local in an older stack frame points to a local in a more recent stack frame. */ { - region_model2 model0; +#if 0 + region_model2 model0 (&mgr); model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); const region2 *q_in_first_frame = model0.get_lvalue (q, NULL); @@ -8950,29 +8946,27 @@ test_state_merging () /* They should be mergeable, and the result should be the same (after canonicalization, at least). */ - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); merged.canonicalize (NULL); ASSERT_EQ (model0, merged); +#endif } /* Verify that we can merge a model in which a local points to a global. */ { - region_model2 model0; + region_model2 model0 (&mgr); model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); model0.set_value (model0.get_lvalue (q, NULL), model0.get_rvalue (addr_of_y, NULL), NULL); - model0.canonicalize (NULL); - region_model2 model1 (model0); ASSERT_EQ (model0, model1); /* They should be mergeable, and the result should be the same (after canonicalization, at least). */ - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); - merged.canonicalize (NULL); ASSERT_EQ (model0, merged); } } @@ -8990,10 +8984,13 @@ test_constraint_merging () tree z = build_global_decl ("z", integer_type_node); tree n = build_global_decl ("n", integer_type_node); + region_model2_manager mgr; test_region_model2_context ctxt; /* model0: 0 <= (x == y) < n. */ - region_model2 model0; + region_model2 model0 (&mgr); + // FIXME: uses "set_to_new_unknown_value" +#if 0 model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt), integer_type_node, &ctxt); model0.add_constraint (x, EQ_EXPR, y, &ctxt); @@ -9001,7 +8998,7 @@ test_constraint_merging () model0.add_constraint (x, LT_EXPR, n, NULL); /* model1: z != 5 && (0 <= x < n). */ - region_model2 model1; + region_model2 model1 (&mgr); model1.set_to_new_unknown_value (model1.get_lvalue (x, &ctxt), integer_type_node, &ctxt); model1.add_constraint (z, NE_EXPR, int_5, NULL); @@ -9010,7 +9007,7 @@ test_constraint_merging () /* They should be mergeable; the merged constraints should be: (0 <= x < n). */ - region_model2 merged; + region_model2 merged (&mgr); ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); ASSERT_EQ (merged.eval_condition (x, GE_EXPR, int_0, &ctxt), @@ -9022,8 +9019,11 @@ test_constraint_merging () tristate (tristate::TS_UNKNOWN)); ASSERT_EQ (merged.eval_condition (x, LT_EXPR, y, &ctxt), tristate (tristate::TS_UNKNOWN)); +#endif } +#if 0 + /* Verify that if we mark a pointer to a malloc-ed region2 as non-NULL, all cast pointers to that region2 are also known to be non-NULL. */ @@ -9380,8 +9380,10 @@ analyzer_region_model2_cc_tests () test_model_equality_3 (); #if 0 test_model_equality_4 (); +#endif test_state_merging (); test_constraint_merging (); +#if 0 test_malloc_constraints (); #endif test_var (); diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h index 91a2f0cfff0..d54e42720b5 100644 --- a/gcc/analyzer/region-model2.h +++ b/gcc/analyzer/region-model2.h @@ -1296,13 +1296,6 @@ private: #if 0 void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE; #endif - -#if 0 - region2_id m_stack_rid; - region2_id m_globals_rid; - region2_id m_code_rid; - region2_id m_heap_rid; -#endif }; } // namespace ana @@ -1837,8 +1830,10 @@ class region_model2 void clobber_region (const region2 *reg); void purge_region (const region2 *reg); void zero_fill_region (const region2 *reg); +#if 0 const svalue2 *set_to_new_unknown_value (const region2 *dst_reg, tree type, region_model2_context *ctxt); +#endif void copy_region (const region2 *dst_reg, const region2 *src_reg, region_model2_context *ctxt); tristate eval_condition (const svalue2 *lhs, @@ -1883,6 +1878,9 @@ class region_model2 { return m_constraints2; } + + region_model2_manager *get_manager () const { return m_mgr; } + #if 0 void get_descendents (region2_id rid, region2_id_set *out, region2_id exclude_rid) const; @@ -1891,13 +1889,10 @@ class region_model2 enum poison_kind pkind, purge_stats *stats, logger *logger); - - bool can_merge_with_p (const region_model2 &other_model, - region_model2 *out_model, - svalue2_id_merger_mapping *out) const; +#endif bool can_merge_with_p (const region_model2 &other_model, region_model2 *out_model) const; - +#if 0 svalue2_id get_value_by_name (const char *name) const; svalue2_id convert_byte_offset_to_array_index (tree ptr_type, diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc index ec98d9e0d01..b6db23ffd6c 100644 --- a/gcc/analyzer/store2.cc +++ b/gcc/analyzer/store2.cc @@ -215,6 +215,44 @@ symbolic_binding2::dump_to_pp (pretty_printer *pp, bool simple) const /* class binding_cluster2. */ +/* FIXME. */ + +binding_cluster2::binding_cluster2 (const binding_cluster2 &other) +: m_base_region (other.m_base_region), m_map (other.m_map), + m_escaped (other.m_escaped) +{ +#if 0 + for (map_t::iterator iter = other.m_map.begin (); iter != other.m_map.end (); + ++iter) + { + const binding_key2 *key = (*iter).first; + const svalue2 *sval = (*iter).second; + m_map.put (key, sval); + } +#endif +} + +/* FIXME. */ + +binding_cluster2& +binding_cluster2::operator= (const binding_cluster2 &other) +{ + gcc_assert (m_base_region == other.m_base_region); + /* For now, assume we only ever copy to an empty cluster. */ + gcc_assert (m_map.elements () == 0); + for (map_t::iterator iter = other.m_map.begin (); iter != other.m_map.end (); + ++iter) + { + const binding_key2 *key = (*iter).first; + const svalue2 *sval = (*iter).second; + m_map.put (key, sval); + } + m_escaped = other.m_escaped; + return *this; +} + +/* FIXME. */ + bool binding_cluster2::operator== (const binding_cluster2 &other) const { @@ -457,6 +495,80 @@ binding_cluster2::remove_overlapping_bindings (store2_manager *mgr, /* FIXME. */ +bool +binding_cluster2::can_merge_p (const binding_cluster2 *cluster_a, + const binding_cluster2 *cluster_b, + binding_cluster2 *out_cluster) +{ + gcc_assert (out_cluster); + + // FIXME: what about m_escaped? + + /* At least one of CLUSTER_A and CLUSTER_B are non-NULL, but either + could be NULL. Handle these cases. */ + if (cluster_a == NULL) + { + gcc_assert (cluster_b != NULL); + gcc_assert (cluster_b->m_base_region == out_cluster->m_base_region); + // FIXME: am not sure what the value should be + gcc_unreachable (); + *out_cluster = *cluster_b; + return true; + } + if (cluster_b == NULL) + { + gcc_assert (cluster_a != NULL); + gcc_assert (cluster_a->m_base_region == out_cluster->m_base_region); + // FIXME: am not sure what the value should be + gcc_unreachable (); + *out_cluster = *cluster_a; + return true; + } + + /* The "both inputs are non-NULL" case. */ + gcc_assert (cluster_a != NULL && cluster_b != NULL); + gcc_assert (cluster_a->m_base_region == out_cluster->m_base_region); + gcc_assert (cluster_b->m_base_region == out_cluster->m_base_region); + + hash_set keys; + for (map_t::iterator iter_a = cluster_a->m_map.begin (); + iter_a != cluster_a->m_map.end (); ++iter_a) + { + const binding_key2 *key_a = (*iter_a).first; + keys.add (key_a); + } + for (map_t::iterator iter_b = cluster_b->m_map.begin (); + iter_b != cluster_b->m_map.end (); ++iter_b) + { + const binding_key2 *key_b = (*iter_b).first; + keys.add (key_b); + } + for (hash_set::iterator iter = keys.begin (); + iter != keys.end (); ++iter) + { + const binding_key2 *key = *iter; + const svalue2 *sval_a = cluster_a->get_any_value (key); + const svalue2 *sval_b = cluster_b->get_any_value (key); + + if (sval_a == sval_b) + { + gcc_assert (sval_a); + out_cluster->m_map.put (key, sval_a); + } + else + { + /* FIXME: TODO: what should we do when we have non-equal + values? What does a merged value look like? */ + return false; + } + } + // FIXME: what about overlaps? + + return true; +} + +/* FIXME. */ + void binding_cluster2::on_escape_to_unknown_fncall (store2_manager *mgr) { @@ -518,6 +630,16 @@ binding_cluster2::get_representative_path_vars (const region2 *base_reg, } } +/* FIXME. */ + +const svalue2 * +binding_cluster2::get_any_value (const binding_key2 *key) const +{ + if (const svalue2 **slot = const_cast (m_map).get (key)) + return *slot; + return NULL; +} + /* class store2_manager. */ /* binding consolidation. */ @@ -606,7 +728,11 @@ get_sorted_parent_regions (auto_vec *out, const region2 *iter_reg; unsigned i; FOR_EACH_VEC_ELT (in, i, iter_reg) - parent_regions.add (iter_reg->get_parent_region ()); + { + const region2 *parent_reg = iter_reg->get_parent_region (); + gcc_assert (parent_reg); + parent_regions.add (parent_reg); + } /* Write to OUT. */ for (hash_set::iterator iter = parent_regions.begin(); @@ -681,6 +807,20 @@ store2::dump_to_pp (pretty_printer *pp, bool summarize, bool multiline) const } } +/* Dump a multiline representation of this store2 to stderr. */ + +DEBUG_FUNCTION void +store2::dump (bool simple) const +{ + 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, simple, true); + pp_newline (&pp); + pp_flush (&pp); +} + const svalue2 * store2::get_any_binding (store2_manager *mgr, const region2 *reg) { @@ -751,6 +891,20 @@ store2::zero_fill_region (store2_manager *mgr, const region2 *reg) /* FIXME. */ +const binding_cluster2 * +store2::get_cluster (const region2 *base_reg) const +{ + gcc_assert (base_reg); + gcc_assert (base_reg->get_base_region () == base_reg); + if (binding_cluster2 **slot + = const_cast (m_cluster_map).get (base_reg)) + return *slot; + else + return NULL; +} + +/* FIXME. */ + binding_cluster2 * store2::get_cluster (const region2 *base_reg) { @@ -779,6 +933,43 @@ store2::get_or_create_cluster (const region2 *base_reg) return cluster; } +/* FIXME. */ + +bool +store2::can_merge_p (const store2 *store_a, const store2 *store_b, + store2 *out_store) +{ + /* Get the union of all base regions for STORE_A and STORE_B. */ + hash_set base_regions; + for (cluster_map_t::iterator iter_a = store_a->m_cluster_map.begin (); + iter_a != store_a->m_cluster_map.end (); ++iter_a) + { + const region2 *base_reg_a = (*iter_a).first; + base_regions.add (base_reg_a); + } + for (cluster_map_t::iterator iter_b = store_b->m_cluster_map.begin (); + iter_b != store_b->m_cluster_map.end (); ++iter_b) + { + const region2 *base_reg_b = (*iter_b).first; + base_regions.add (base_reg_b); + } + + for (hash_set::iterator iter = base_regions.begin (); + iter != base_regions.end (); ++iter) + { + const region2 *base_reg = *iter; + const binding_cluster2 *cluster_a = store_a->get_cluster (base_reg); + const binding_cluster2 *cluster_b = store_b->get_cluster (base_reg); + /* At least one of cluster_a and cluster_b must be non-NULL. */ + binding_cluster2 *out_cluster + = out_store->get_or_create_cluster (base_reg); + if (!binding_cluster2::can_merge_p (cluster_a, cluster_b, + out_cluster)) + return false; + } + return true; +} + /* FIXME. For use when handling an unrecognized function call. */ /* FIXME: do we want to mark the cluster as having escaped? diff --git a/gcc/analyzer/store2.h b/gcc/analyzer/store2.h index 4b3dd3bc45e..952b274acc0 100644 --- a/gcc/analyzer/store2.h +++ b/gcc/analyzer/store2.h @@ -184,6 +184,8 @@ class binding_cluster2 public: binding_cluster2 (const region2 *base_region) : m_base_region (base_region), m_map (), m_escaped (false) {} + binding_cluster2 (const binding_cluster2 &other); + binding_cluster2& operator=(const binding_cluster2 &other); bool operator== (const binding_cluster2 &other) const; bool operator!= (const binding_cluster2 &other) const @@ -209,6 +211,7 @@ public: void remove_overlapping_bindings (store2_manager *mgr, const region2 *reg); +#if 1 template void for_each_value (void (*cb) (const svalue2 *sval, T user_data), T user_data) @@ -216,6 +219,11 @@ public: for (map_t::iterator iter = m_map.begin (); iter != m_map.end (); ++iter) cb ((*iter).second, user_data); } +#endif + + static bool can_merge_p (const binding_cluster2 *cluster_a, + const binding_cluster2 *cluster_b, + binding_cluster2 *out_cluster); void on_escape_to_unknown_fncall (store2_manager *mgr); @@ -228,6 +236,7 @@ public: auto_vec *out_pvs) const; private: + const svalue2 *get_any_value (const binding_key2 *key) const; #if 0 void get_overlapping_bindings (store2_manager *mgr, const region2 *reg, auto_vec *out); @@ -263,6 +272,8 @@ private: class store2 { public: + typedef hash_map cluster_map_t; + store2 (); store2 (const store2 &other); ~store2 (); @@ -274,6 +285,7 @@ public: } void dump_to_pp (pretty_printer *pp, bool summarize, bool multiline) const; + void dump (bool simple) const; const svalue2 *get_direct_binding (store2_manager *mgr, const region2 *reg); const svalue2 *get_default_binding (store2_manager *mgr, const region2 *reg); @@ -285,6 +297,7 @@ public: void purge_region (store2_manager *mgr, const region2 *reg); void zero_fill_region (store2_manager *mgr, const region2 *reg); + const binding_cluster2 *get_cluster (const region2 *base_reg) const; binding_cluster2 *get_cluster (const region2 *base_reg); binding_cluster2 *get_or_create_cluster (const region2 *base_reg); @@ -297,6 +310,9 @@ public: cb ((*iter).first, user_data); } + static bool can_merge_p (const store2 *store_a, const store2 *store_b, + store2 *out_store); + void on_escape_to_unknown_fncall (store2_manager *mgr, const region2 *base_reg); @@ -305,10 +321,12 @@ public: void get_representative_path_vars (const svalue2 *sval, auto_vec *out_pvs) const; + cluster_map_t::iterator begin () const { return m_cluster_map.begin (); } + cluster_map_t::iterator end () const { return m_cluster_map.end (); } + private: void remove_overlapping_bindings (store2_manager *mgr, const region2 *reg); - typedef hash_map cluster_map_t; cluster_map_t m_cluster_map; // TODO: deep copy of this! }; -- 2.21.0