From 02a26fa636faeeeb56fe1128f7ead48d1ae65920 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 13 Apr 2020 19:14:27 -0400 Subject: [PATCH 025/179] FIXME: WIP towards binding values to regions --- gcc/analyzer/analyzer.h | 1 + gcc/analyzer/region-model2.cc | 295 ++++++++++++++++++++++++---------- gcc/analyzer/region-model2.h | 95 +++++++++-- 3 files changed, 292 insertions(+), 99 deletions(-) diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 6a37c0a3262..bad6e07f0ff 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -64,6 +64,7 @@ class region2; class map_region2; class array_region2; class symbolic_region2; + class field_region2; class region_model2; class region_model2_context; diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc index 8911a42cc7a..15e6302f5da 100644 --- a/gcc/analyzer/region-model2.cc +++ b/gcc/analyzer/region-model2.cc @@ -65,6 +65,10 @@ along with GCC; see the file COPYING3. If not see namespace ana { +static region2 * +make_region_for_type (region2 *parent_reg, tree type, + region_model2_context *ctxt); + /* Dump T to PP in language-independent form, for debugging/logging/dumping purposes. */ @@ -213,6 +217,20 @@ svalue2::dump_dot_to_pp (const region_model2 &model, } #endif +/* FIXME. */ + +DEBUG_FUNCTION void +svalue2::dump () 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); + pp_newline (&pp); + pp_flush (&pp); +} + /* If this svalue2 is a constant_svalue2, return the underlying tree constant. Otherwise return NULL_TREE. */ @@ -227,6 +245,18 @@ svalue2::maybe_get_constant () const /* class region_svalue2 : public svalue2. */ +/* FIXME. */ + +void +region_svalue2::dump_to_pp (pretty_printer *pp) const +{ + pp_string (pp, "region_svalue2("); + print_quoted_type (pp, get_type ()); + pp_string (pp, ", "); + m_reg->dump_to_pp (pp); + pp_string (pp, ")"); +} + /* Compare the fields of this region_svalue2 with OTHER, returning true if they are equal. For use by svalue2::operator==. */ @@ -433,6 +463,18 @@ region_svalue2::eval_condition (region_svalue2 *lhs, /* class constant_svalue2 : public svalue2. */ +/* FIXME. */ + +void +constant_svalue2::dump_to_pp (pretty_printer *pp) const +{ + pp_string (pp, "constant_svalue2("); + print_quoted_type (pp, get_type ()); + pp_string (pp, ", "); + dump_tree (pp, m_cst_expr); + pp_string (pp, ")"); +} + /* Compare the fields of this constant_svalue2 with OTHER, returning true if they are equal. For use by svalue2::operator==. */ @@ -655,22 +697,20 @@ region2::kind_to_str (enum kind kind) After comparing base class fields and kind, the rest of the comparison is handled off to a "compare_fields" member function specific to the appropriate subclass. */ -#if 0 + bool region2::operator== (const region2 &other) const { - if (m_parent_reg != other.m_parent_reg) - return false; - if (m_sval_id != other.m_sval_id) + if (m_parent != other.m_parent) return false; if (m_type != other.m_type) return false; - enum region2_kind this_kind = get_kind (); - enum region2_kind other_kind = other.get_kind (); + enum kind this_kind = get_kind (); + enum kind other_kind = other.get_kind (); if (this_kind != other_kind) return false; - +#if 0 /* Compare views. */ if (m_view_regs.length () != other.m_view_regs.length ()) return false; @@ -679,11 +719,12 @@ region2::operator== (const region2 &other) const FOR_EACH_VEC_ELT (m_view_regs, i, rid) if (! (*rid == other.m_view_regs[i])) return false; - +#endif switch (this_kind) { default: gcc_unreachable (); +#if 0 case RK_PRIMITIVE: { #if 1 @@ -784,9 +825,17 @@ region2::operator== (const region2 &other) const = (const heap_region2 &)other; return this_sub.compare_fields (other_sub); } +#endif + case RK_FIELD: + { + const field_region2 &this_sub + = (const field_region2 &)*this; + const field_region2 &other_sub + = (const field_region2 &)other; + return this_sub.compare_fields (other_sub); + } } } -#endif /* Set this region2's value to RHS_SVAL (or potentially a variant of it, for some kinds of casts). */ @@ -1127,7 +1176,7 @@ region_model2::copy_array_region2 (region2_id dst_reg, /* Generate a hash value for this region2. The work is done by the add_to_hash vfunc. */ -#if 0 + hashval_t region2::hash () const { @@ -1135,7 +1184,6 @@ region2::hash () const add_to_hash (hstate); return hstate.end (); } -#endif /* Print a one-liner representation of this region2 to PP, assuming that this region2 is within MODEL and its id is THIS_REG. */ @@ -1260,6 +1308,24 @@ region2::dump_to_pp (const region_model2 &model, } #endif +void +region2::dump_to_pp (pretty_printer *pp) const +{ + pp_string (pp, "region2()"); // TODO: eliminate this base class impl +} + +DEBUG_FUNCTION void +region2::dump () 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); + pp_newline (&pp); + pp_flush (&pp); +} + /* Base implementation of region2::dump_child_label vfunc. */ #if 0 void @@ -1366,14 +1432,13 @@ region2::region2 (const region2 &other) /* Base implementation of region2::add_to_hash vfunc; subclasses should chain up to this. */ -#if 0 + void region2::add_to_hash (inchash::hash &hstate) const { hstate.add_ptr (m_parent); hstate.add_ptr (m_type); } -#endif /* Base implementation of region2::print_fields vfunc. */ #if 0 @@ -3014,6 +3079,82 @@ symbolic_region2::print_fields (const region_model2 &model, } #endif +void +field_region2::add_to_hash (inchash::hash &hstate) const +{ + region2::add_to_hash (hstate); + hstate.add_ptr (m_field); +} + +bool +field_region2::compare_fields (const field_region2 &other) const +{ + return m_field == other.m_field; +} + +void +field_region2::dump_to_pp (pretty_printer *pp) const +{ + pp_string (pp, "field_region2("); + get_parent_region ()->dump_to_pp (pp); + pp_string (pp, ", "); + print_quoted_type (pp, get_type ()); + pp_printf (pp, ", %qE)", m_field); +} + +/* class region_model2_manager. */ + +region_model2_manager::region_model2_manager () +: m_root_region (), + m_globals_region (&m_root_region), + m_globals_map () +{ + // TODO +} + +/* Return an svalue2 *for a constant_svalue2 for CST_EXPR, + creating the constant_svalue2 if necessary. + The constant_svalue2 instances are reused, based on pointer equality + of trees */ +svalue2 * +region_model2_manager::get_or_create_constant_svalue2 (tree cst_expr) +{ + gcc_assert (cst_expr); + + constant_svalue2 **slot = m_constants_map.get (cst_expr); + if (slot) + return *slot; + constant_svalue2 *cst_sval = new constant_svalue2 (cst_expr); + m_constants_map.put (cst_expr, cst_sval); + return cst_sval; + // TODO: cst equality vs tree pointer equality? +} + +/* FIXME. */ + +region2 * +region_model2_manager::get_region_for_global (tree expr, + region_model2_context *ctxt) +{ + gcc_assert (TREE_CODE (expr) == VAR_DECL); + + region2 **slot = m_globals_map.get (expr); + if (slot) + return *slot; + region2 *reg = make_region_for_type (&m_globals_region, TREE_TYPE (expr), + ctxt); + m_globals_map.put (expr, reg); + return reg; +} + +region2 * +region_model2_manager::get_field_region (region2 *parent, tree field, + region_model2_context *ctxt) +{ + region2 *field_reg = new field_region2 (parent, field); + return const_cast (m_fields.consolidate (field_reg)); +} + /* class region_model2. */ /* region_model2's default ctor. */ @@ -3250,7 +3391,18 @@ region_model2::dump_dot (const char *path) const void region_model2::dump_to_pp (pretty_printer *pp, bool summarize) const { - gcc_unreachable (); + // TODO: sort into some deterministic order + for (store_map_t::iterator iter = m_store_map.begin (); + iter != m_store_map.end (); ++iter) + { + if (iter != m_store_map.begin ()) + pp_string (pp, ", "); + region2 *reg = (*iter).first; + svalue2 *sval = (*iter).second; + reg->dump_to_pp (pp); + pp_string (pp, ": "); + sval->dump_to_pp (pp); + } #if 0 if (summarize) { @@ -4358,15 +4510,17 @@ region_model2::get_lvalue_1 (region_model2_manager *mgr, path_var pv, gcc_assert (expr); - gcc_unreachable (); -#if 0 switch (TREE_CODE (expr)) { default: + gcc_unreachable (); // FIXME +#if 0 return make_region2_for_unexpected_tree_code (ctxt, expr, dump_location_t ()); +#endif +#if 0 case ARRAY_REF: { tree array = TREE_OPERAND (expr, 0); @@ -4414,18 +4568,11 @@ region_model2::get_lvalue_1 (region_model2_manager *mgr, path_var pv, offset_sval, ctxt); } break; - +#endif case VAR_DECL: /* Handle globals. */ if (is_global_var (expr)) - { - region2 *globals_reg - = get_root_region2 ()->ensure_globals_region2 (this); - map_region2 *globals = get_region2 (globals_reg); - region2 *var_reg = globals->get_or_create (this, globals_reg, expr, - TREE_TYPE (expr), ctxt); - return var_reg; - } + return mgr->get_region_for_global (expr, ctxt); /* Fall through. */ @@ -4438,15 +4585,14 @@ region_model2::get_lvalue_1 (region_model2_manager *mgr, path_var pv, || TREE_CODE (expr) == VAR_DECL || TREE_CODE (expr) == RESULT_DECL); - int stack_depth = pv.m_stack_depth; - stack_region2 *stack = get_root_region2 ()->get_stack_region2 (this); - gcc_assert (stack); - region2 *frame_reg = stack->get_frame_reg (stack_depth); - frame_region2 *frame = get_region2 (frame_reg); + frame_region2 *frame = get_current_frame (); gcc_assert (frame); + gcc_unreachable (); // TODO +#if 0 region2 *child_reg = frame->get_or_create (this, frame_reg, expr, TREE_TYPE (expr), ctxt); return child_reg; +#endif } case COMPONENT_REF: @@ -4455,17 +4601,24 @@ region_model2::get_lvalue_1 (region_model2_manager *mgr, path_var pv, tree obj = TREE_OPERAND (expr, 0); tree field = TREE_OPERAND (expr, 1); tree obj_type = TREE_TYPE (obj); + // FIXME: +#if 0 if (TREE_CODE (obj_type) != RECORD_TYPE && TREE_CODE (obj_type) != UNION_TYPE) return make_region2_for_unexpected_tree_code (ctxt, obj_type, dump_location_t ()); - region2 *obj_reg = get_lvalue (obj, ctxt); - region2 *struct_or_union_reg - = get_or_create_view (obj_reg, TREE_TYPE (obj), ctxt); +#endif + region2 *obj_reg = get_lvalue (mgr, obj, ctxt); +#if 0 + region2 *struct_or_union_reg = as_a (obj_reg); +#endif + return mgr->get_field_region (obj_reg, field, ctxt); +#if 0 return get_field_region2 (struct_or_union_reg, field, ctxt); +#endif } break; - +#if 0 case CONST_DECL: { tree cst_type = TREE_TYPE (expr); @@ -4498,8 +4651,8 @@ region_model2::get_lvalue_1 (region_model2_manager *mgr, path_var pv, ctxt); }; break; - } #endif + } } /* If we see a tree code we don't know how to handle, rather than @@ -4567,18 +4720,19 @@ region_model2::get_rvalue_1 (region_model2_manager *mgr, path_var pv, { gcc_assert (pv.m_tree); - gcc_unreachable (); - -#if 0 switch (TREE_CODE (pv.m_tree)) { default: { + gcc_unreachable (); +#if 0 svalue2 *unknown_sval = new unknown_svalue2 (TREE_TYPE (pv.m_tree)); return add_svalue2 (unknown_sval); +#endif } break; +#if 0 case ADDR_EXPR: { /* "&EXPR". */ @@ -4598,12 +4752,12 @@ region_model2::get_rvalue_1 (region_model2_manager *mgr, path_var pv, region2 *element_reg = get_lvalue (pv, ctxt); return get_region2 (element_reg)->get_value (*this, true, ctxt); } - +#endif case INTEGER_CST: case REAL_CST: case STRING_CST: - return get_or_create_constant_svalue2 (pv.m_tree); - + return mgr->get_or_create_constant_svalue2 (pv.m_tree); +#if 0 case COMPONENT_REF: case MEM_REF: case SSA_NAME: @@ -4614,8 +4768,8 @@ region_model2::get_rvalue_1 (region_model2_manager *mgr, path_var pv, region2 *var_reg = get_lvalue (pv, ctxt); return get_region2 (var_reg)->get_value (*this, true, ctxt); } - } #endif + } } /* Get the value of PV within this region_model2, @@ -4667,28 +4821,7 @@ region_model2::get_or_create_ptr_svalue2 (tree ptr_type, region2 *reg) return add_svalue2 (new region_svalue2 (ptr_type, rid)); } #endif -/* Return an svalue2 *for a constant_svalue2 for CST_EXPR, - creating the constant_svalue2 if necessary. - The constant_svalue2 instances are reused, based on pointer equality - of trees */ -#if 0 -svalue2_id -region_model2::get_or_create_constant_svalue2 (tree cst_expr) -{ - gcc_assert (cst_expr); - - /* Reuse one if it already exists. */ - // TODO: maybe store a map, rather than do linear search? - int i; - svalue2 *svalue2; - FOR_EACH_VEC_ELT (m_svalue2s, i, svalue2) - if (svalue2->maybe_get_constant () == cst_expr) - return svalue2_id::from_int (i); - svalue2 *cst_sval = add_svalue2 (new constant_svalue2 (cst_expr)); - return cst_sval; -} -#endif /* Return an svalue2 *for a region_svalue2 for FNDECL, creating the function_region2 if necessary. */ #if 0 @@ -4881,8 +5014,8 @@ region_model2::maybe_cast (tree dst_type, svalue2 *sval, return the child region2's region2_id. */ #if 0 region2_id -region_model2::get_field_region2 (region2 *struct_or_union_reg, tree field, - region_model2_context *ctxt) +region_model2::get_field_region (region2 *struct_or_union_reg, tree field, + region_model2_context *ctxt) { struct_or_union_region2 *sou_reg = get_region2 (struct_or_union_reg); @@ -4998,8 +5131,8 @@ region_model2::set_value (region2 *lhs_reg, svalue2 *rhs_sval, gcc_assert (lhs_reg); gcc_assert (rhs_sval); - // TODO: - gcc_unreachable (); + m_store_map.put (lhs_reg, rhs_sval); + // TODO: various kinds of invalidation etc } /* Set the value of the region2 given by LHS to the value given @@ -5919,9 +6052,10 @@ region_model2::replace_svalue2 (svalue2 *sval, svalue2 *new_sval) /* Make a region2 of an appropriate subclass for TYPE, with parent PARENT_REG, or return NULL for types we don't yet know how to handle. */ -#if 0 + static region2 * -make_region2_for_type (region2 *parent_reg, tree type) +make_region_for_type (region2 *parent_reg, tree type, + region_model2_context *ctxt) { gcc_assert (TYPE_P (type)); @@ -5948,26 +6082,14 @@ make_region2_for_type (region2 *parent_reg, tree type) if (VOID_TYPE_P (type)) return new symbolic_region2 (parent_reg, type, false); - return NULL; -} -#endif -/* Add a region2 with type TYPE and parent PARENT_REG. */ -#if 0 -region2_id -region_model2::add_region2_for_type (region2 *parent_reg, tree type, - region_model2_context *ctxt) -{ - gcc_assert (TYPE_P (type)); - - if (region2 *new_region2 = make_region2_for_type (parent_reg, type)) - return add_region2 (new_region2); - /* If we can't handle TYPE, return a placeholder region2, and stop exploring this path. */ + gcc_unreachable (); +#if 0 return make_region2_for_unexpected_tree_code (ctxt, type, - dump_location_t ()); -} + dump_location_t ()); #endif +} /* Helper class for region_model2::purge_unused_svalue2s. */ @@ -6866,6 +6988,9 @@ assert_dump_eq (const location &loc, auto_fix_quotes sentinel; pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; + + return; // FIXME + model.dump_to_pp (&pp, summarize); ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected); } diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h index 1849858f95d..69905fb9d94 100644 --- a/gcc/analyzer/region-model2.h +++ b/gcc/analyzer/region-model2.h @@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_REGION_MODEL_2_H #define GCC_ANALYZER_REGION_MODEL_2_H +#include "analyzer/uniq-manager.h" // FIXME + /* Implementation of the region-based ternary model described in: "A Memory Model for Static Analysis of C Programs" (Zhongxing Xu, Ted Kremenek, and Jian Zhang) @@ -167,6 +169,9 @@ public: pretty_printer *pp) const; #endif + virtual void dump_to_pp (pretty_printer *pp) const = 0; + void dump () const; + virtual region_svalue2 *dyn_cast_region_svalue2 () { return NULL; } virtual constant_svalue2 *dyn_cast_constant_svalue2 () { return NULL; } virtual const constant_svalue2 *dyn_cast_constant_svalue2 () const @@ -216,6 +221,8 @@ public: FINAL OVERRIDE; #endif + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; + region_svalue2 *dyn_cast_region_svalue2 () FINAL OVERRIDE { return this; } region2 * get_pointee () const { return m_reg; } @@ -275,6 +282,7 @@ public: #endif enum kind get_kind () const FINAL OVERRIDE { return SK_CONSTANT; } + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; #if 0 void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE; @@ -336,6 +344,7 @@ public: #endif enum kind get_kind () const FINAL OVERRIDE { return SK_UNKNOWN; } + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; #if 0 void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE; @@ -382,6 +391,7 @@ public: #endif enum kind get_kind () const FINAL OVERRIDE { return SK_POISONED; } + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; #if 0 void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE; @@ -452,6 +462,7 @@ public: #endif enum kind get_kind () const FINAL OVERRIDE { return SK_SETJMP; } + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; #if 0 void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE; @@ -494,7 +505,8 @@ public: heap_region (RK_HEAP) root_region (RK_ROOT) label_region (RK_FUNCTION) - symbolic_region (RK_SYMBOLIC). */ + symbolic_region (RK_SYMBOLIC) + field_region (RK_FIELD). */ /* Abstract base class representing a chunk of memory. @@ -527,14 +539,15 @@ public: RK_STACK, RK_HEAP, RK_ROOT, - RK_SYMBOLIC + RK_SYMBOLIC, + RK_FIELD }; static const char *kind_to_str (enum kind); virtual ~region2 () {} - bool operator== (const region &other) const; - bool operator!= (const region &other) const { return !(*this == other); } + bool operator== (const region2 &other) const; + bool operator!= (const region2 &other) const { return !(*this == other); } #if 0 virtual region2 *clone () const = 0; @@ -575,6 +588,10 @@ public: pretty_printer *pp, const char *prefix, bool is_last_child) const; + + virtual void dump_to_pp (pretty_printer *pp) const; // = 0; + void dump () const; + #if 0 virtual void dump_child_label (const region_model2 &model, region2 *child, @@ -598,8 +615,8 @@ public: region2 (const region2 &other); #endif -#if 0 virtual void add_to_hash (inchash::hash &hstate) const; +#if 0 virtual void print_fields (const region_model2 &model, pretty_printer *pp) const; #endif @@ -1350,12 +1367,66 @@ public: }; /* FIXME. */ - + +class field_region2 : public region2 +{ +public: + field_region2 (region2 *parent, tree field) + : region2 (parent, TREE_TYPE (field)), m_field (field) + {} + +#if 0 + region2 *clone () const FINAL OVERRIDE; +#endif + + bool compare_fields (const field_region2 &other) const; + + enum region2::kind get_kind () const FINAL OVERRIDE { return RK_FIELD; } + + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; + +protected: + void add_to_hash (inchash::hash &hstate) const OVERRIDE; + +private: + tree m_field; +}; + +/* FIXME. */ + class region_model2_manager { public: - function_region2 *get_region2_for_fndecl (tree fndecl, region_model2_context *ctxt); - region2 *get_region2_for_label (tree label, region_model2_context *ctxt); + region_model2_manager (); + + svalue2 *get_or_create_constant_svalue2 (tree cst_expr); + + function_region2 *get_region_for_fndecl (tree fndecl, + region_model2_context *ctxt); + region2 *get_region_for_label (tree label, + region_model2_context *ctxt); + region2 *get_region_for_global (tree expr, + region_model2_context *ctxt); + region2 *get_field_region (region2 *parent, tree field, + region_model2_context *ctxt); + +private: + root_region2 m_root_region; + + typedef hash_map constants_map_t; + constants_map_t m_constants_map; + + globals_region2 m_globals_region; + typedef hash_map globals_map_t; + typedef globals_map_t::iterator globals_iterator_t; + globals_map_t m_globals_map; + + uniq_manager m_fields; +#if 0 + struct field_key_t { region2 *m_parent; tree m_field; }; + typedef hash_map fields_map_t; + fields_map_t m_fields_map; +#endif }; /* A region_model2 encapsulates a representation of the state of memory, with @@ -1462,7 +1533,6 @@ class region_model2 #if 0 svalue2_id get_or_create_ptr_svalue2 (tree ptr_type, region2_id id); - svalue2_id get_or_create_constant_svalue2 (tree cst_expr); svalue2_id get_svalue2_for_fndecl (tree ptr_type, tree fndecl, region_model2_context *ctxt); svalue2_id get_svalue2_for_label (tree ptr_type, tree label, @@ -1615,12 +1685,9 @@ class region_model2 bool *is_first); #endif - /* TODO: map from region2 to svalue2. */ - + typedef hash_map store_map_t; + store_map_t m_store_map; #if 0 - auto_delete_vec m_svalue2s; - auto_delete_vec m_region2s; - region2_id m_root_rid; constraint_manager *m_constraints; // TODO: embed, rather than dynalloc? #endif frame_region2 *m_current_frame; -- 2.21.0