| /* Regions of memory. |
| Copyright (C) 2019-2022 Free Software Foundation, Inc. |
| Contributed by David Malcolm <dmalcolm@redhat.com>. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #ifndef GCC_ANALYZER_REGION_H |
| #define GCC_ANALYZER_REGION_H |
| |
| #include "analyzer/complexity.h" |
| |
| namespace ana { |
| |
| /* An enum for identifying different spaces within memory. */ |
| |
| enum memory_space |
| { |
| MEMSPACE_UNKNOWN, |
| MEMSPACE_CODE, |
| MEMSPACE_GLOBALS, |
| MEMSPACE_STACK, |
| MEMSPACE_HEAP, |
| MEMSPACE_READONLY_DATA |
| }; |
| |
| /* An enum for discriminating between the different concrete subclasses |
| of region. */ |
| |
| enum region_kind |
| { |
| RK_FRAME, |
| RK_GLOBALS, |
| RK_CODE, |
| RK_FUNCTION, |
| RK_LABEL, |
| RK_STACK, |
| RK_HEAP, |
| RK_ROOT, |
| RK_SYMBOLIC, |
| RK_DECL, |
| RK_FIELD, |
| RK_ELEMENT, |
| RK_OFFSET, |
| RK_SIZED, |
| RK_CAST, |
| RK_HEAP_ALLOCATED, |
| RK_ALLOCA, |
| RK_STRING, |
| RK_BIT_RANGE, |
| RK_UNKNOWN |
| }; |
| |
| /* Region and its subclasses. |
| |
| The class hierarchy looks like this (using indentation to show |
| inheritance, and with region_kinds shown for the concrete subclasses): |
| |
| region |
| space_region |
| frame_region (RK_FRAME) |
| globals_region (RK_GLOBALS) |
| code_region (RK_CODE) |
| stack_region (RK_STACK) |
| heap_region (RK_HEAP) |
| root_region (RK_ROOT) |
| function_region (RK_FUNCTION) |
| label_region (RK_LABEL) |
| symbolic_region (RK_SYMBOLIC) |
| decl_region (RK_DECL), |
| field_region (RK_FIELD) |
| element_region (RK_ELEMENT) |
| offset_region (RK_OFFSET) |
| sized_region (RK_SIZED) |
| cast_region (RK_CAST) |
| heap_allocated_region (RK_HEAP_ALLOCATED) |
| alloca_region (RK_ALLOCA) |
| string_region (RK_STRING) |
| bit_range_region (RK_BIT_RANGE) |
| unknown_region (RK_UNKNOWN). */ |
| |
| /* Abstract base class for representing ways of accessing chunks of memory. |
| |
| Regions form a tree-like hierarchy, with a root region at the base, |
| with memory space regions within it, representing the stack and |
| globals, with frames within the stack, and regions for variables |
| within the frames and the "globals" region. Regions for structs |
| can have subregions for fields. */ |
| |
| class region |
| { |
| public: |
| virtual ~region (); |
| |
| unsigned get_id () const { return m_id; } |
| static int cmp_ids (const region *reg1, const region *reg2); |
| |
| virtual enum region_kind get_kind () const = 0; |
| virtual const frame_region * |
| dyn_cast_frame_region () const { return NULL; } |
| virtual const function_region * |
| dyn_cast_function_region () const { return NULL; } |
| virtual const symbolic_region * |
| dyn_cast_symbolic_region () const { return NULL; } |
| virtual const decl_region * |
| dyn_cast_decl_region () const { return NULL; } |
| virtual const field_region * |
| dyn_cast_field_region () const { return NULL; } |
| virtual const element_region * |
| dyn_cast_element_region () const { return NULL; } |
| virtual const offset_region * |
| dyn_cast_offset_region () const { return NULL; } |
| virtual const sized_region * |
| dyn_cast_sized_region () const { return NULL; } |
| virtual const cast_region * |
| dyn_cast_cast_region () const { return NULL; } |
| virtual const string_region * |
| dyn_cast_string_region () const { return NULL; } |
| virtual const bit_range_region * |
| dyn_cast_bit_range_region () const { return NULL; } |
| |
| virtual void accept (visitor *v) const; |
| |
| const region *get_parent_region () const { return m_parent; } |
| const region *get_base_region () const; |
| bool base_region_p () const; |
| bool descendent_of_p (const region *elder) const; |
| const frame_region *maybe_get_frame_region () const; |
| enum memory_space get_memory_space () const; |
| bool can_have_initial_svalue_p () const; |
| |
| tree maybe_get_decl () const; |
| |
| tree get_type () const { return m_type; } |
| |
| void print (const region_model &model, |
| pretty_printer *pp) const; |
| label_text get_desc (bool simple=true) const; |
| |
| virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0; |
| void dump (bool simple) const; |
| |
| json::value *to_json () const; |
| |
| bool non_null_p () const; |
| |
| static int cmp_ptr_ptr (const void *, const void *); |
| |
| bool involves_p (const svalue *sval) const; |
| |
| region_offset get_offset () const; |
| |
| /* Attempt to get the size of this region as a concrete number of bytes. |
| If successful, return true and write the size to *OUT. |
| Otherwise return false. */ |
| virtual bool get_byte_size (byte_size_t *out) const; |
| |
| /* Attempt to get the size of this region as a concrete number of bits. |
| If successful, return true and write the size to *OUT. |
| Otherwise return false. */ |
| virtual bool get_bit_size (bit_size_t *out) const; |
| |
| /* Get a symbolic value describing the size of this region in bytes |
| (which could be "unknown"). */ |
| virtual const svalue *get_byte_size_sval (region_model_manager *mgr) const; |
| |
| /* Attempt to get the offset in bits of this region relative to its parent. |
| If successful, return true and write to *OUT. |
| Otherwise return false. */ |
| virtual bool get_relative_concrete_offset (bit_offset_t *out) const; |
| |
| /* Attempt to get the position and size of this region expressed as a |
| concrete range of bytes relative to its parent. |
| If successful, return true and write to *OUT. |
| Otherwise return false. */ |
| bool get_relative_concrete_byte_range (byte_range *out) const; |
| |
| void |
| get_subregions_for_binding (region_model_manager *mgr, |
| bit_offset_t start_bit_offset, |
| bit_size_t size_in_bits, |
| tree type, |
| auto_vec <const region *> *out) const; |
| |
| bool symbolic_for_unknown_ptr_p () const; |
| |
| /* For most base regions it makes sense to track the bindings of the region |
| within the store. As an optimization, some are not tracked (to avoid |
| bloating the store object with redundant binding clusters). */ |
| virtual bool tracked_p () const { return true; } |
| |
| const complexity &get_complexity () const { return m_complexity; } |
| |
| bool is_named_decl_p (const char *decl_name) const; |
| |
| protected: |
| region (complexity c, unsigned id, const region *parent, tree type); |
| |
| private: |
| region_offset calc_offset () const; |
| |
| complexity m_complexity; |
| unsigned m_id; // purely for deterministic sorting at this stage, for dumps |
| const region *m_parent; |
| tree m_type; |
| |
| mutable region_offset *m_cached_offset; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const region *>::test (const region *) |
| { |
| return true; |
| } |
| |
| namespace ana { |
| |
| /* Abstract subclass of region, for regions that represent an untyped |
| space within memory, such as the stack or the heap. */ |
| |
| class space_region : public region |
| { |
| protected: |
| space_region (unsigned id, const region *parent) |
| : region (complexity (parent), id, parent, NULL_TREE) |
| {} |
| }; |
| |
| /* Concrete space_region subclass, representing a function frame on the stack, |
| to contain the locals. |
| The parent is the stack region; there's also a hierarchy of call-stack |
| prefixes expressed via m_calling_frame. |
| For example, given "oldest" calling "middle" called "newest" we would have |
| - a stack depth of 3 |
| - frame (A) for "oldest" with index 0 for depth 1, calling_frame == NULL |
| - frame (B) for "middle" with index 1 for depth 2, calling_frame == (A) |
| - frame (C) for "newest" with index 2 for depth 3, calling_frame == (B) |
| where the parent region for each of the frames is the "stack" region. |
| The index is the count of frames earlier than this in the stack. */ |
| |
| class frame_region : public space_region |
| { |
| public: |
| /* A support class for uniquifying instances of frame_region. */ |
| struct key_t |
| { |
| key_t (const frame_region *calling_frame, function *fun) |
| : m_calling_frame (calling_frame), m_fun (fun) |
| { |
| /* calling_frame can be NULL. */ |
| gcc_assert (fun); |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_calling_frame); |
| hstate.add_ptr (m_fun); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_calling_frame == other.m_calling_frame && m_fun == other.m_fun); |
| } |
| |
| void mark_deleted () { m_fun = reinterpret_cast<function *> (1); } |
| void mark_empty () { m_fun = NULL; } |
| bool is_deleted () const |
| { |
| return m_fun == reinterpret_cast<function *> (1); |
| } |
| bool is_empty () const { return m_fun == NULL; } |
| |
| const frame_region *m_calling_frame; |
| function *m_fun; |
| }; |
| |
| frame_region (unsigned id, const region *parent, |
| const frame_region *calling_frame, |
| function *fun, int index) |
| : space_region (id, parent), m_calling_frame (calling_frame), |
| m_fun (fun), m_index (index) |
| {} |
| ~frame_region (); |
| |
| /* region vfuncs. */ |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_FRAME; } |
| const frame_region * dyn_cast_frame_region () const FINAL OVERRIDE |
| { |
| return this; |
| } |
| void accept (visitor *v) const FINAL OVERRIDE; |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| /* Accessors. */ |
| const frame_region *get_calling_frame () const { return m_calling_frame; } |
| function *get_function () const { return m_fun; } |
| tree get_fndecl () const { return get_function ()->decl; } |
| int get_index () const { return m_index; } |
| int get_stack_depth () const { return m_index + 1; } |
| |
| const decl_region * |
| get_region_for_local (region_model_manager *mgr, |
| tree expr, |
| const region_model_context *ctxt) const; |
| |
| unsigned get_num_locals () const { return m_locals.elements (); } |
| |
| /* Implemented in region-model-manager.cc. */ |
| void dump_untracked_regions () const; |
| |
| private: |
| const frame_region *m_calling_frame; |
| function *m_fun; |
| int m_index; |
| |
| /* The regions for the decls within this frame are managed by this |
| object, rather than the region_model_manager, to make it a simple |
| lookup by tree. */ |
| typedef hash_map<tree, decl_region *> map_t; |
| map_t m_locals; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const frame_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_FRAME; |
| } |
| |
| template <> struct default_hash_traits<frame_region::key_t> |
| : public member_function_hash_traits<frame_region::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* Concrete space_region subclass, to hold global variables (data and bss). */ |
| |
| class globals_region : public space_region |
| { |
| public: |
| globals_region (unsigned id, const region *parent) |
| : space_region (id, parent) |
| {} |
| |
| /* region vfuncs. */ |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_GLOBALS; } |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const globals_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_GLOBALS; |
| } |
| |
| namespace ana { |
| |
| /* Concrete space_region subclass, representing the code segment |
| containing functions. */ |
| |
| class code_region : public space_region |
| { |
| public: |
| code_region (unsigned id, const region *parent) |
| : space_region (id, parent) |
| {} |
| |
| /* region vfuncs. */ |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_CODE; } |
| |
| const region *get_element (region_model *model, |
| const svalue *index, |
| region_model_context *ctxt); |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const code_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_CODE; |
| } |
| |
| namespace ana { |
| |
| /* Concrete region subclass. A region representing the code for |
| a particular function. */ |
| |
| class function_region : public region |
| { |
| public: |
| function_region (unsigned id, const code_region *parent, tree fndecl) |
| : region (complexity (parent), id, parent, TREE_TYPE (fndecl)), |
| m_fndecl (fndecl) |
| { |
| gcc_assert (FUNC_OR_METHOD_TYPE_P (TREE_TYPE (fndecl))); |
| } |
| |
| /* region vfuncs. */ |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_FUNCTION; } |
| const function_region * |
| dyn_cast_function_region () const FINAL OVERRIDE{ return this; } |
| |
| tree get_fndecl () const { return m_fndecl; } |
| |
| region *get_element (region_model *model, |
| const svalue *index_sid, |
| region_model_context *ctxt); |
| |
| private: |
| tree m_fndecl; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const function_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_FUNCTION; |
| } |
| |
| namespace ana { |
| |
| /* Concrete region subclass. A region representing a particular label |
| within a function. */ |
| |
| class label_region : public region |
| { |
| public: |
| label_region (unsigned id, const function_region *parent, tree label) |
| : region (complexity (parent), id, parent, NULL_TREE), m_label (label) |
| { |
| gcc_assert (TREE_CODE (label) == LABEL_DECL); |
| } |
| |
| /* region vfuncs. */ |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_LABEL; } |
| |
| tree get_label () const { return m_label; } |
| |
| private: |
| tree m_label; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const label_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_LABEL; |
| } |
| |
| namespace ana { |
| |
| /* Concrete space_region subclass representing a stack, containing all stack |
| frames. */ |
| |
| class stack_region : public space_region |
| { |
| public: |
| stack_region (unsigned id, region *parent) |
| : space_region (id, parent) |
| {} |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_STACK; } |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const stack_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_STACK; |
| } |
| |
| namespace ana { |
| |
| /* Concrete space_region subclass: a region within which regions can be |
| dynamically allocated. */ |
| |
| class heap_region : public space_region |
| { |
| public: |
| heap_region (unsigned id, region *parent) |
| : space_region (id, parent) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_HEAP; } |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const heap_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_HEAP; |
| } |
| |
| namespace ana { |
| |
| /* Concrete region subclass. The root region, containing all regions |
| (either directly, or as descendents). |
| Unique within a region_model_manager. */ |
| |
| class root_region : public region |
| { |
| public: |
| root_region (unsigned id); |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_ROOT; } |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const root_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_ROOT; |
| } |
| |
| namespace ana { |
| |
| /* Concrete region subclass: a region to use when dereferencing an unknown |
| pointer. */ |
| |
| class symbolic_region : public region |
| { |
| public: |
| /* A support class for uniquifying instances of symbolic_region. */ |
| struct key_t |
| { |
| key_t (const region *parent, const svalue *sval_ptr) |
| : m_parent (parent), m_sval_ptr (sval_ptr) |
| { |
| gcc_assert (sval_ptr); |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_parent); |
| hstate.add_ptr (m_sval_ptr); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_parent == other.m_parent && m_sval_ptr == other.m_sval_ptr); |
| } |
| |
| void mark_deleted () { m_sval_ptr = reinterpret_cast<const svalue *> (1); } |
| void mark_empty () { m_sval_ptr = NULL; } |
| bool is_deleted () const |
| { |
| return m_sval_ptr == reinterpret_cast<const svalue *> (1); |
| } |
| bool is_empty () const { return m_sval_ptr == NULL; } |
| |
| const region *m_parent; |
| const svalue *m_sval_ptr; |
| }; |
| |
| symbolic_region (unsigned id, region *parent, const svalue *sval_ptr); |
| |
| const symbolic_region * |
| dyn_cast_symbolic_region () const FINAL OVERRIDE { return this; } |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_SYMBOLIC; } |
| void accept (visitor *v) const FINAL OVERRIDE; |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| const svalue *get_pointer () const { return m_sval_ptr; } |
| |
| private: |
| const svalue *m_sval_ptr; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const symbolic_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_SYMBOLIC; |
| } |
| |
| template <> struct default_hash_traits<symbolic_region::key_t> |
| : public member_function_hash_traits<symbolic_region::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* Concrete region subclass representing the memory occupied by a |
| variable (whether for a global or a local). |
| Also used for representing SSA names, as if they were locals. */ |
| |
| class decl_region : public region |
| { |
| public: |
| decl_region (unsigned id, const region *parent, tree decl) |
| : region (complexity (parent), id, parent, TREE_TYPE (decl)), m_decl (decl), |
| m_tracked (calc_tracked_p (decl)) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_DECL; } |
| const decl_region * |
| dyn_cast_decl_region () const FINAL OVERRIDE { return this; } |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| bool tracked_p () const FINAL OVERRIDE { return m_tracked; } |
| |
| tree get_decl () const { return m_decl; } |
| int get_stack_depth () const; |
| |
| const svalue *maybe_get_constant_value (region_model_manager *mgr) const; |
| const svalue *get_svalue_for_constructor (tree ctor, |
| region_model_manager *mgr) const; |
| const svalue *get_svalue_for_initializer (region_model_manager *mgr) const; |
| |
| private: |
| static bool calc_tracked_p (tree decl); |
| |
| tree m_decl; |
| |
| /* Cached result of calc_tracked_p, so that we can quickly determine when |
| we don't to track a binding_cluster for this decl (to avoid bloating |
| store objects). |
| This can be debugged using -fdump-analyzer-untracked. */ |
| bool m_tracked; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const decl_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_DECL; |
| } |
| |
| namespace ana { |
| |
| /* Concrete region subclass representing the memory occupied by a |
| field within a struct or union. */ |
| |
| class field_region : public region |
| { |
| public: |
| /* A support class for uniquifying instances of field_region. */ |
| struct key_t |
| { |
| key_t (const region *parent, tree field) |
| : m_parent (parent), m_field (field) |
| { |
| gcc_assert (field); |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_parent); |
| hstate.add_ptr (m_field); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_parent == other.m_parent && m_field == other.m_field); |
| } |
| |
| void mark_deleted () { m_field = reinterpret_cast<tree> (1); } |
| void mark_empty () { m_field = NULL_TREE; } |
| bool is_deleted () const { return m_field == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_field == NULL_TREE; } |
| |
| const region *m_parent; |
| tree m_field; |
| }; |
| |
| field_region (unsigned id, const region *parent, tree field) |
| : region (complexity (parent), id, parent, TREE_TYPE (field)), |
| m_field (field) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_FIELD; } |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| const field_region * |
| dyn_cast_field_region () const FINAL OVERRIDE { return this; } |
| |
| tree get_field () const { return m_field; } |
| |
| bool get_relative_concrete_offset (bit_offset_t *out) const FINAL OVERRIDE; |
| |
| private: |
| tree m_field; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const field_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_FIELD; |
| } |
| |
| template <> struct default_hash_traits<field_region::key_t> |
| : public member_function_hash_traits<field_region::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* An element within an array. */ |
| |
| class element_region : public region |
| { |
| public: |
| /* A support class for uniquifying instances of element_region. */ |
| struct key_t |
| { |
| key_t (const region *parent, tree element_type, const svalue *index) |
| : m_parent (parent), m_element_type (element_type), m_index (index) |
| { |
| gcc_assert (index); |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_parent); |
| hstate.add_ptr (m_element_type); |
| hstate.add_ptr (m_index); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_parent == other.m_parent |
| && m_element_type == other.m_element_type |
| && m_index == other.m_index); |
| } |
| |
| void mark_deleted () { m_index = reinterpret_cast<const svalue *> (1); } |
| void mark_empty () { m_index = NULL; } |
| bool is_deleted () const |
| { |
| return m_index == reinterpret_cast<const svalue *> (1); |
| } |
| bool is_empty () const { return m_index == NULL; } |
| |
| const region *m_parent; |
| tree m_element_type; |
| const svalue *m_index; |
| }; |
| |
| element_region (unsigned id, const region *parent, tree element_type, |
| const svalue *index) |
| : region (complexity::from_pair (parent, index), id, parent, element_type), |
| m_index (index) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_ELEMENT; } |
| const element_region * |
| dyn_cast_element_region () const FINAL OVERRIDE { return this; } |
| |
| void accept (visitor *v) const FINAL OVERRIDE; |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| const svalue *get_index () const { return m_index; } |
| |
| virtual bool |
| get_relative_concrete_offset (bit_offset_t *out) const FINAL OVERRIDE; |
| |
| private: |
| const svalue *m_index; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const element_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_ELEMENT; |
| } |
| |
| template <> struct default_hash_traits<element_region::key_t> |
| : public member_function_hash_traits<element_region::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* A byte-offset within another region, for handling pointer arithmetic |
| as a region. */ |
| |
| class offset_region : public region |
| { |
| public: |
| /* A support class for uniquifying instances of offset_region. */ |
| struct key_t |
| { |
| key_t (const region *parent, tree element_type, const svalue *byte_offset) |
| : m_parent (parent), m_element_type (element_type), m_byte_offset (byte_offset) |
| { |
| gcc_assert (byte_offset); |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_parent); |
| hstate.add_ptr (m_element_type); |
| hstate.add_ptr (m_byte_offset); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_parent == other.m_parent |
| && m_element_type == other.m_element_type |
| && m_byte_offset == other.m_byte_offset); |
| } |
| |
| void mark_deleted () { m_byte_offset = reinterpret_cast<const svalue *> (1); } |
| void mark_empty () { m_byte_offset = NULL; } |
| bool is_deleted () const |
| { |
| return m_byte_offset == reinterpret_cast<const svalue *> (1); |
| } |
| bool is_empty () const { return m_byte_offset == NULL; } |
| |
| const region *m_parent; |
| tree m_element_type; |
| const svalue *m_byte_offset; |
| }; |
| |
| offset_region (unsigned id, const region *parent, tree type, |
| const svalue *byte_offset) |
| : region (complexity::from_pair (parent, byte_offset), id, parent, type), |
| m_byte_offset (byte_offset) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_OFFSET; } |
| const offset_region * |
| dyn_cast_offset_region () const FINAL OVERRIDE { return this; } |
| |
| void accept (visitor *v) const FINAL OVERRIDE; |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| const svalue *get_byte_offset () const { return m_byte_offset; } |
| |
| bool get_relative_concrete_offset (bit_offset_t *out) const FINAL OVERRIDE; |
| |
| private: |
| const svalue *m_byte_offset; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const offset_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_OFFSET; |
| } |
| |
| template <> struct default_hash_traits<offset_region::key_t> |
| : public member_function_hash_traits<offset_region::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* A region that is size BYTES_SIZE_SVAL in size within its parent |
| region (or possibly larger, which would lead to an overflow. */ |
| |
| class sized_region : public region |
| { |
| public: |
| /* A support class for uniquifying instances of sized_region. */ |
| struct key_t |
| { |
| key_t (const region *parent, tree element_type, |
| const svalue *byte_size_sval) |
| : m_parent (parent), m_element_type (element_type), |
| m_byte_size_sval (byte_size_sval) |
| { |
| gcc_assert (byte_size_sval); |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_parent); |
| hstate.add_ptr (m_element_type); |
| hstate.add_ptr (m_byte_size_sval); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_parent == other.m_parent |
| && m_element_type == other.m_element_type |
| && m_byte_size_sval == other.m_byte_size_sval); |
| } |
| |
| void mark_deleted () { m_byte_size_sval = reinterpret_cast<const svalue *> (1); } |
| void mark_empty () { m_byte_size_sval = NULL; } |
| bool is_deleted () const |
| { |
| return m_byte_size_sval == reinterpret_cast<const svalue *> (1); |
| } |
| bool is_empty () const { return m_byte_size_sval == NULL; } |
| |
| const region *m_parent; |
| tree m_element_type; |
| const svalue *m_byte_size_sval; |
| const svalue *m_end_offset; |
| }; |
| |
| sized_region (unsigned id, const region *parent, tree type, |
| const svalue *byte_size_sval) |
| : region (complexity::from_pair (parent, byte_size_sval), |
| id, parent, type), |
| m_byte_size_sval (byte_size_sval) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_SIZED; } |
| const sized_region * |
| dyn_cast_sized_region () const FINAL OVERRIDE { return this; } |
| |
| void accept (visitor *v) const FINAL OVERRIDE; |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| bool get_byte_size (byte_size_t *out) const FINAL OVERRIDE; |
| bool get_bit_size (bit_size_t *out) const FINAL OVERRIDE; |
| |
| const svalue * |
| get_byte_size_sval (region_model_manager *) const FINAL OVERRIDE |
| { |
| return m_byte_size_sval; |
| } |
| |
| private: |
| const svalue *m_byte_size_sval; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const sized_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_SIZED; |
| } |
| |
| template <> struct default_hash_traits<sized_region::key_t> |
| : public member_function_hash_traits<sized_region::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* A region that views another region using a different type. */ |
| |
| class cast_region : public region |
| { |
| public: |
| /* A support class for uniquifying instances of cast_region. */ |
| struct key_t |
| { |
| key_t (const region *original_region, tree type) |
| : m_original_region (original_region), m_type (type) |
| { |
| gcc_assert (type); |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_original_region); |
| hstate.add_ptr (m_type); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_original_region == other.m_original_region |
| && m_type == other.m_type); |
| } |
| |
| void mark_deleted () { m_type = reinterpret_cast<tree> (1); } |
| void mark_empty () { m_type = NULL_TREE; } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == NULL_TREE; } |
| |
| const region *m_original_region; |
| tree m_type; |
| }; |
| |
| cast_region (unsigned id, const region *original_region, tree type) |
| : region (complexity (original_region), id, |
| original_region->get_parent_region (), type), |
| m_original_region (original_region) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_CAST; } |
| const cast_region * |
| dyn_cast_cast_region () const FINAL OVERRIDE { return this; } |
| void accept (visitor *v) const FINAL OVERRIDE; |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| const region *get_original_region () const { return m_original_region; } |
| |
| private: |
| const region *m_original_region; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const cast_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_CAST; |
| } |
| |
| template <> struct default_hash_traits<cast_region::key_t> |
| : public member_function_hash_traits<cast_region::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* An untyped region dynamically allocated on the heap via "malloc" |
| or similar. */ |
| |
| class heap_allocated_region : public region |
| { |
| public: |
| heap_allocated_region (unsigned id, const region *parent) |
| : region (complexity (parent), id, parent, NULL_TREE) |
| {} |
| |
| enum region_kind |
| get_kind () const FINAL OVERRIDE { return RK_HEAP_ALLOCATED; } |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| }; |
| |
| /* An untyped region dynamically allocated on the stack via "alloca". */ |
| |
| class alloca_region : public region |
| { |
| public: |
| alloca_region (unsigned id, const frame_region *parent) |
| : region (complexity (parent), id, parent, NULL_TREE) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_ALLOCA; } |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| }; |
| |
| /* A region for a STRING_CST. */ |
| |
| class string_region : public region |
| { |
| public: |
| string_region (unsigned id, const region *parent, tree string_cst) |
| : region (complexity (parent), id, parent, TREE_TYPE (string_cst)), |
| m_string_cst (string_cst) |
| {} |
| |
| const string_region * |
| dyn_cast_string_region () const FINAL OVERRIDE { return this; } |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_STRING; } |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| tree get_string_cst () const { return m_string_cst; } |
| |
| private: |
| tree m_string_cst; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const string_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_STRING; |
| } |
| |
| namespace ana { |
| |
| /* A region for a specific range of bits within another region. */ |
| |
| class bit_range_region : public region |
| { |
| public: |
| /* A support class for uniquifying instances of bit_range_region. */ |
| struct key_t |
| { |
| key_t (const region *parent, tree type, const bit_range &bits) |
| : m_parent (parent), m_type (type), m_bits (bits) |
| { |
| gcc_assert (parent); |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_parent); |
| hstate.add_ptr (m_type); |
| hstate.add_wide_int (m_bits.m_start_bit_offset); |
| hstate.add_wide_int (m_bits.m_size_in_bits); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_parent == other.m_parent |
| && m_type == other.m_type |
| && m_bits == other.m_bits); |
| } |
| |
| void mark_deleted () { m_parent = reinterpret_cast<const region *> (1); } |
| void mark_empty () { m_parent = NULL; } |
| bool is_deleted () const |
| { |
| return m_parent == reinterpret_cast<const region *> (1); |
| } |
| bool is_empty () const { return m_parent == NULL; } |
| |
| const region *m_parent; |
| tree m_type; |
| bit_range m_bits; |
| }; |
| |
| bit_range_region (unsigned id, const region *parent, tree type, |
| const bit_range &bits) |
| : region (complexity (parent), id, parent, type), |
| m_bits (bits) |
| {} |
| |
| const bit_range_region * |
| dyn_cast_bit_range_region () const FINAL OVERRIDE { return this; } |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_BIT_RANGE; } |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| |
| const bit_range &get_bits () const { return m_bits; } |
| |
| bool get_byte_size (byte_size_t *out) const FINAL OVERRIDE; |
| bool get_bit_size (bit_size_t *out) const FINAL OVERRIDE; |
| const svalue *get_byte_size_sval (region_model_manager *mgr) const FINAL OVERRIDE; |
| bool get_relative_concrete_offset (bit_offset_t *out) const FINAL OVERRIDE; |
| |
| private: |
| bit_range m_bits; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const bit_range_region *>::test (const region *reg) |
| { |
| return reg->get_kind () == RK_BIT_RANGE; |
| } |
| |
| template <> struct default_hash_traits<bit_range_region::key_t> |
| : public member_function_hash_traits<bit_range_region::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* An unknown region, for handling unimplemented tree codes. */ |
| |
| class unknown_region : public region |
| { |
| public: |
| unknown_region (unsigned id, const region *parent, tree type) |
| : region (complexity (parent), id, parent, type) |
| {} |
| |
| enum region_kind get_kind () const FINAL OVERRIDE { return RK_UNKNOWN; } |
| |
| void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; |
| }; |
| |
| } // namespace ana |
| |
| #endif /* GCC_ANALYZER_REGION_H */ |