analyzer/bar-chart.o \
analyzer/call-string.o \
analyzer/checker-path.o \
+ analyzer/complexity.o \
analyzer/constraint-manager.o \
analyzer/diagnostic-manager.o \
analyzer/engine.o \
class rewind_info_t;
class engine;
+class state_machine;
+class logger;
+class visitor;
/* Forward decls of functions. */
--- /dev/null
+/* Measuring the complexity of svalues/regions.
+ Copyright (C) 2020 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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "diagnostic-core.h"
+#include "gimple-pretty-print.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "diagnostic-core.h"
+#include "graphviz.h"
+#include "options.h"
+#include "cgraph.h"
+#include "tree-dfa.h"
+#include "stringpool.h"
+#include "convert.h"
+#include "target.h"
+#include "fold-const.h"
+#include "tree-pretty-print.h"
+#include "tristate.h"
+#include "bitmap.h"
+#include "selftest.h"
+#include "function.h"
+#include "json.h"
+#include "analyzer/analyzer.h"
+#include "analyzer/analyzer-logging.h"
+#include "options.h"
+#include "cgraph.h"
+#include "cfg.h"
+#include "digraph.h"
+#include "analyzer/call-string.h"
+#include "analyzer/program-point.h"
+#include "analyzer/store.h"
+#include "analyzer/complexity.h"
+#include "analyzer/svalue.h"
+#include "analyzer/region.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+/* struct complexity. */
+
+/* Get complexity for a new node that references REG
+ (the complexity of REG, plus one for the new node). */
+
+complexity::complexity (const region *reg)
+: m_num_nodes (reg->get_complexity ().m_num_nodes + 1),
+ m_max_depth (reg->get_complexity ().m_max_depth + 1)
+{
+}
+
+/* Get complexity for a new node that references SVAL.
+ (the complexity of SVAL, plus one for the new node). */
+
+complexity::complexity (const svalue *sval)
+: m_num_nodes (sval->get_complexity ().m_num_nodes + 1),
+ m_max_depth (sval->get_complexity ().m_max_depth + 1)
+{
+}
+
+/* Get complexity for a new node that references nodes with complexity
+ C1 and C2. */
+
+complexity
+complexity::from_pair (const complexity &c1, const complexity &c2)
+{
+ return complexity (c1.m_num_nodes + c2.m_num_nodes + 1,
+ MAX (c1.m_max_depth, c2.m_max_depth) + 1);
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
--- /dev/null
+/* Measuring the complexity of svalues/regions.
+ Copyright (C) 2020 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_COMPLEXITY_H
+#define GCC_ANALYZER_COMPLEXITY_H
+
+namespace ana {
+
+/* A measurement of the complexity of an svalue or region, so that
+ we can impose bounds on the growth of these tree-like structures
+ and thus avoid infinite chains of analysis. */
+
+struct complexity
+{
+ complexity (unsigned num_nodes, unsigned max_depth)
+ : m_num_nodes (num_nodes), m_max_depth (max_depth)
+ {}
+
+ complexity (const region *reg);
+ complexity (const svalue *sval);
+ static complexity from_pair (const complexity &c1, const complexity &c);
+
+ /* The total number of svalues and regions in the tree of this
+ entity, including the entity itself. */
+ unsigned m_num_nodes;
+
+ /* The maximum depth of the tree of this entity, including the
+ entity itself. */
+ unsigned m_max_depth;
+};
+
+} // namespace ana
+
+#endif /* GCC_ANALYZER_COMPLEXITY_H */
(Zhongxing Xu, Ted Kremenek, and Jian Zhang)
http://lcs.ios.ac.cn/~xuzb/canalyze/memmodel.pdf */
+#include "analyzer/svalue.h"
+#include "analyzer/region.h"
+
using namespace ana;
namespace inchash
int m_num_client_items;
};
-/* A measurement of the complexity of an svalue or region, so that
- we can impose bounds on the growth of these tree-like structures
- and thus avoid infinite chains of analysis. */
-
-struct complexity
-{
- complexity (unsigned num_nodes, unsigned max_depth)
- : m_num_nodes (num_nodes), m_max_depth (max_depth)
- {}
-
- complexity (const region *reg);
- complexity (const svalue *sval);
- static complexity from_pair (const complexity &c1, const complexity &c);
-
- /* The total number of svalues and regions in the tree of this
- entity, including the entity itself. */
- unsigned m_num_nodes;
-
- /* The maximum depth of the tree of this entity, including the
- entity itself. */
- unsigned m_max_depth;
-};
-
/* A base class for visiting regions and svalues, with do-nothing
base implementations of the per-subclass vfuncs. */
virtual void visit_region (const region *) {}
};
-/* An enum for discriminating between the different concrete subclasses
- of svalue. */
-
-enum svalue_kind
-{
- SK_REGION,
- SK_CONSTANT,
- SK_UNKNOWN,
- SK_POISONED,
- SK_SETJMP,
- SK_INITIAL,
- SK_UNARYOP,
- SK_BINOP,
- SK_SUB,
- SK_UNMERGEABLE,
- SK_PLACEHOLDER,
- SK_WIDENING,
- SK_COMPOUND,
- SK_CONJURED
-};
-
-/* svalue and its subclasses.
-
- The class hierarchy looks like this (using indentation to show
- inheritance, and with svalue_kinds shown for the concrete subclasses):
-
- svalue
- region_svalue (SK_REGION): a pointer to a region
- constant_svalue (SK_CONSTANT): a constant
- unknown_svalue (SK_UNKNOWN): an unknowable value
- poisoned_svalue (SK_POISONED): a unusable value (undefined)
- setjmp_svalue (SK_SETJMP): a setjmp/longjmp buffer
- initial_svalue (SK_INITIAL): the initial value of a region
- unaryop_svalue (SK_UNARYOP): unary operation on another svalue
- binop_svalue (SK_BINOP): binary operation on two svalues
- sub_svalue (SK_SUB): the result of accessing a subregion
- unmergeable_svalue (SK_UNMERGEABLE): a value that is so interesting
- from a control-flow perspective that it can inhibit state-merging
- placeholder_svalue (SK_PLACEHOLDER): for use in selftests.
- widening_svalue (SK_WIDENING): a merger of two svalues (possibly
- in an iteration).
- compound_svalue (SK_COMPOUND): a mapping of bit-ranges to svalues
- conjured_svalue (SK_CONJURED): a value arising from a stmt. */
-
-/* An abstract base class representing a value held by a region of memory. */
-
-class svalue
-{
-public:
- virtual ~svalue () {}
-
- tree get_type () const { return m_type; }
-
- virtual enum svalue_kind get_kind () const = 0;
-
- void print (const region_model &model,
- pretty_printer *pp) const;
-
- virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
- void dump (bool simple=true) const;
- label_text get_desc (bool simple=true) const;
-
- json::value *to_json () const;
-
- virtual const region_svalue *
- dyn_cast_region_svalue () const { return NULL; }
- virtual const constant_svalue *
- dyn_cast_constant_svalue () const { return NULL; }
- virtual const poisoned_svalue *
- dyn_cast_poisoned_svalue () const { return NULL; }
- virtual const setjmp_svalue *
- dyn_cast_setjmp_svalue () const { return NULL; }
- virtual const initial_svalue *
- dyn_cast_initial_svalue () const { return NULL; }
- virtual const unaryop_svalue *
- dyn_cast_unaryop_svalue () const { return NULL; }
- virtual const binop_svalue *
- dyn_cast_binop_svalue () const { return NULL; }
- virtual const sub_svalue *
- dyn_cast_sub_svalue () const { return NULL; }
- virtual const unmergeable_svalue *
- dyn_cast_unmergeable_svalue () const { return NULL; }
- virtual const widening_svalue *
- dyn_cast_widening_svalue () const { return NULL; }
- virtual const compound_svalue *
- dyn_cast_compound_svalue () const { return NULL; }
- virtual const conjured_svalue *
- dyn_cast_conjured_svalue () const { return NULL; }
-
- tree maybe_get_constant () const;
- const svalue *maybe_undo_cast () const;
- const svalue *unwrap_any_unmergeable () const;
-
- const svalue *can_merge_p (const svalue *other,
- region_model_manager *mgr,
- model_merger *merger) const;
-
- const complexity &get_complexity () const { return m_complexity; }
-
- virtual void accept (visitor *v) const = 0;
-
- bool live_p (const svalue_set &live_svalues,
- const region_model *model) const;
- virtual bool implicitly_live_p (const svalue_set &live_svalues,
- const region_model *model) const;
-
- static int cmp_ptr (const svalue *, const svalue *);
- static int cmp_ptr_ptr (const void *, const void *);
-
- protected:
- svalue (complexity c, tree type)
- : m_complexity (c), m_type (type)
- {}
-
- private:
- complexity m_complexity;
- tree m_type;
-};
-
-/* Concrete subclass of svalue representing a pointer value that points to
- a known region */
-
-class region_svalue : public svalue
-{
-public:
- /* A support class for uniquifying instances of region_svalue. */
- struct key_t
- {
- key_t (tree type, const region *reg)
- : m_type (type), m_reg (reg)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- hstate.add_ptr (m_type);
- hstate.add_ptr (m_reg);
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_type == other.m_type && m_reg == other.m_reg);
- }
-
- 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; }
-
- tree m_type;
- const region *m_reg;
- };
-
- region_svalue (tree type, const region *reg)
- : svalue (complexity (reg), type),
- m_reg (reg)
- {
- gcc_assert (m_reg != NULL);
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_REGION; }
- const region_svalue *
- dyn_cast_region_svalue () const FINAL OVERRIDE { return this; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
-
- const region * get_pointee () const { return m_reg; }
-
- static tristate eval_condition (const region_svalue *lhs_ptr,
- enum tree_code op,
- const region_svalue *rhs_ptr);
-
- private:
- const region *m_reg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const region_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_REGION;
-}
-
-template <> struct default_hash_traits<region_svalue::key_t>
-: public member_function_hash_traits<region_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue representing a specific constant value. */
-
-class constant_svalue : public svalue
-{
-public:
- constant_svalue (tree cst_expr)
- : svalue (complexity (1, 1), TREE_TYPE (cst_expr)), m_cst_expr (cst_expr)
- {
- gcc_assert (cst_expr);
- gcc_assert (CONSTANT_CLASS_P (cst_expr));
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_CONSTANT; }
- const constant_svalue *
- dyn_cast_constant_svalue () const FINAL OVERRIDE { return this; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
- bool implicitly_live_p (const svalue_set &,
- const region_model *) const FINAL OVERRIDE;
-
- tree get_constant () const { return m_cst_expr; }
- static tristate eval_condition (const constant_svalue *lhs,
- enum tree_code op,
- const constant_svalue *rhs);
-
- private:
- tree m_cst_expr;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const constant_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_CONSTANT;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue representing an unknowable value, the bottom
- value when thinking of svalues as a lattice.
- This is a singleton (w.r.t. its manager): there is a single unknown_svalue
- per type. Self-comparisons of such instances yield "unknown". */
-
-class unknown_svalue : public svalue
-{
-public:
- unknown_svalue (tree type)
- : svalue (complexity (1, 1), type)
- {}
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_UNKNOWN; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
-};
-
-/* An enum describing a particular kind of "poisoned" value. */
-
-enum poison_kind
-{
- /* For use to describe freed memory. */
- POISON_KIND_FREED,
-
- /* For use on pointers to regions within popped stack frames. */
- POISON_KIND_POPPED_STACK
-};
-
-extern const char *poison_kind_to_str (enum poison_kind);
-
-/* Concrete subclass of svalue representing a value that should not
- be used (e.g. uninitialized memory, freed memory). */
-
-class poisoned_svalue : public svalue
-{
-public:
- /* A support class for uniquifying instances of poisoned_svalue. */
- struct key_t
- {
- key_t (enum poison_kind kind, tree type)
- : m_kind (kind), m_type (type)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- hstate.add_int (m_kind);
- hstate.add_ptr (m_type);
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_kind == other.m_kind && 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; }
-
- enum poison_kind m_kind;
- tree m_type;
- };
-
- poisoned_svalue (enum poison_kind kind, tree type)
- : svalue (complexity (1, 1), type), m_kind (kind) {}
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_POISONED; }
- const poisoned_svalue *
- dyn_cast_poisoned_svalue () const FINAL OVERRIDE { return this; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
-
- enum poison_kind get_poison_kind () const { return m_kind; }
-
- private:
- enum poison_kind m_kind;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const poisoned_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_POISONED;
-}
-
-template <> struct default_hash_traits<poisoned_svalue::key_t>
-: public member_function_hash_traits<poisoned_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* A bundle of information recording a setjmp/sigsetjmp call, corresponding
- roughly to a jmp_buf. */
-
-struct setjmp_record
-{
- setjmp_record (const exploded_node *enode,
- const gcall *setjmp_call)
- : m_enode (enode), m_setjmp_call (setjmp_call)
- {
- }
-
- bool operator== (const setjmp_record &other) const
- {
- return (m_enode == other.m_enode
- && m_setjmp_call == other.m_setjmp_call);
- }
-
- void add_to_hash (inchash::hash *hstate) const
- {
- hstate->add_ptr (m_enode);
- hstate->add_ptr (m_setjmp_call);
- }
-
- static int cmp (const setjmp_record &rec1, const setjmp_record &rec2);
-
- const exploded_node *m_enode;
- const gcall *m_setjmp_call;
-};
-
-/* Concrete subclass of svalue representing buffers for setjmp/sigsetjmp,
- so that longjmp/siglongjmp can potentially "return" to an entirely
- different function. */
-
-class setjmp_svalue : public svalue
-{
-public:
- /* A support class for uniquifying instances of poisoned_svalue. */
- struct key_t
- {
- key_t (const setjmp_record &record, tree type)
- : m_record (record), m_type (type)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- m_record.add_to_hash (&hstate);
- hstate.add_ptr (m_type);
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_record == other.m_record && 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; }
-
- setjmp_record m_record;
- tree m_type;
- };
-
- setjmp_svalue (const setjmp_record &setjmp_record,
- tree type)
- : svalue (complexity (1, 1), type), m_setjmp_record (setjmp_record)
- {}
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_SETJMP; }
- const setjmp_svalue *
- dyn_cast_setjmp_svalue () const FINAL OVERRIDE { return this; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
-
- int get_enode_index () const;
-
- const setjmp_record &get_setjmp_record () const { return m_setjmp_record; }
-
- private:
- setjmp_record m_setjmp_record;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const setjmp_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_SETJMP;
-}
-
-template <> struct default_hash_traits<setjmp_svalue::key_t>
-: public member_function_hash_traits<setjmp_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue representing the initial value of a
- specific region.
-
- This represents the initial value at the start of the analysis path,
- as opposed to the first time the region is accessed during the path.
- Hence as soon as we have a call to an unknown function, all previously
- unmodelled globals become implicitly "unknown" rathen than "initial". */
-
-class initial_svalue : public svalue
-{
-public:
- initial_svalue (tree type, const region *reg)
- : svalue (complexity (reg), type), m_reg (reg)
- {
- gcc_assert (m_reg != NULL);
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_INITIAL; }
- const initial_svalue *
- dyn_cast_initial_svalue () const FINAL OVERRIDE { return this; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
- bool implicitly_live_p (const svalue_set &,
- const region_model *) const FINAL OVERRIDE;
-
- const region *get_region () const { return m_reg; }
-
- private:
- const region *m_reg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const initial_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_INITIAL;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue representing a unary operation on
- another svalues (e.g. a cast). */
-
-class unaryop_svalue : public svalue
-{
-public:
- /* A support class for uniquifying instances of unaryop_svalue. */
- struct key_t
- {
- key_t (tree type, enum tree_code op, const svalue *arg)
- : m_type (type), m_op (op), m_arg (arg)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- hstate.add_ptr (m_type);
- hstate.add_int (m_op);
- hstate.add_ptr (m_arg);
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_type == other.m_type
- && m_op == other.m_op
- && m_arg == other.m_arg);
- }
-
- 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; }
-
- tree m_type;
- enum tree_code m_op;
- const svalue *m_arg;
- };
-
- unaryop_svalue (tree type, enum tree_code op, const svalue *arg)
- : svalue (complexity (arg), type), m_op (op), m_arg (arg)
- {
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_UNARYOP; }
- const unaryop_svalue *
- dyn_cast_unaryop_svalue () const FINAL OVERRIDE { return this; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
- bool implicitly_live_p (const svalue_set &,
- const region_model *) const FINAL OVERRIDE;
-
- enum tree_code get_op () const { return m_op; }
- const svalue *get_arg () const { return m_arg; }
-
- private:
- enum tree_code m_op;
- const svalue *m_arg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const unaryop_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_UNARYOP;
-}
-
-template <> struct default_hash_traits<unaryop_svalue::key_t>
-: public member_function_hash_traits<unaryop_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue representing a binary operation of
- two svalues. */
-
-class binop_svalue : public svalue
-{
-public:
- /* A support class for uniquifying instances of binop_svalue. */
- struct key_t
- {
- key_t (tree type, enum tree_code op,
- const svalue *arg0, const svalue *arg1)
- : m_type (type), m_op (op), m_arg0 (arg0), m_arg1 (arg1)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- hstate.add_ptr (m_type);
- hstate.add_int (m_op);
- hstate.add_ptr (m_arg0);
- hstate.add_ptr (m_arg1);
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_type == other.m_type
- && m_op == other.m_op
- && m_arg0 == other.m_arg0
- && m_arg1 == other.m_arg1);
- }
-
- 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; }
-
- tree m_type;
- enum tree_code m_op;
- const svalue *m_arg0;
- const svalue *m_arg1;
- };
-
- binop_svalue (tree type, enum tree_code op,
- const svalue *arg0, const svalue *arg1)
- : svalue (complexity::from_pair (arg0->get_complexity (),
- arg1->get_complexity ()),
- type),
- m_op (op), m_arg0 (arg0), m_arg1 (arg1)
- {
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_BINOP; }
- const binop_svalue *dyn_cast_binop_svalue () const FINAL OVERRIDE
- {
- return this;
- }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
- bool implicitly_live_p (const svalue_set &,
- const region_model *) const FINAL OVERRIDE;
-
- enum tree_code get_op () const { return m_op; }
- const svalue *get_arg0 () const { return m_arg0; }
- const svalue *get_arg1 () const { return m_arg1; }
-
- private:
- enum tree_code m_op;
- const svalue *m_arg0;
- const svalue *m_arg1;
-};
-
} // namespace ana
-template <>
-template <>
-inline bool
-is_a_helper <const binop_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_BINOP;
-}
-
-template <> struct default_hash_traits<binop_svalue::key_t>
-: public member_function_hash_traits<binop_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
namespace ana {
-/* Concrete subclass of svalue representing the result of accessing a subregion
- of another svalue (the value of a component/field of a struct, or an element
- from an array). */
-
-class sub_svalue : public svalue
-{
-public:
- /* A support class for uniquifying instances of sub_svalue. */
- struct key_t
- {
- key_t (tree type, const svalue *parent_svalue, const region *subregion)
- : m_type (type), m_parent_svalue (parent_svalue), m_subregion (subregion)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- hstate.add_ptr (m_type);
- hstate.add_ptr (m_parent_svalue);
- hstate.add_ptr (m_subregion);
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_type == other.m_type
- && m_parent_svalue == other.m_parent_svalue
- && m_subregion == other.m_subregion);
- }
-
- 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; }
-
- tree m_type;
- const svalue *m_parent_svalue;
- const region *m_subregion;
- };
- sub_svalue (tree type, const svalue *parent_svalue,
- const region *subregion);
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_SUB; }
- const sub_svalue *dyn_cast_sub_svalue () const FINAL OVERRIDE
- {
- return this;
- }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
- bool implicitly_live_p (const svalue_set &,
- const region_model *) const FINAL OVERRIDE;
-
- const svalue *get_parent () const { return m_parent_svalue; }
- const region *get_subregion () const { return m_subregion; }
-
- private:
- const svalue *m_parent_svalue;
- const region *m_subregion;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const sub_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_SUB;
-}
-
-template <> struct default_hash_traits<sub_svalue::key_t>
-: public member_function_hash_traits<sub_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue: decorate another svalue,
- so that the resulting svalue can be identified as being
- "interesting to control flow".
- For example, consider the return value from setjmp. We
- don't want to merge states in which the result is 0 with
- those in which the result is non-zero. By using an
- unmergeable_svalue for the result, we can inhibit such merges
- and have separate exploded nodes for those states, keeping
- the first and second returns from setjmp distinct in the exploded
- graph. */
-
-class unmergeable_svalue : public svalue
-{
-public:
- unmergeable_svalue (const svalue *arg)
- : svalue (complexity (arg), arg->get_type ()), m_arg (arg)
- {
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_UNMERGEABLE; }
- const unmergeable_svalue *
- dyn_cast_unmergeable_svalue () const FINAL OVERRIDE { return this; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
- bool implicitly_live_p (const svalue_set &,
- const region_model *) const FINAL OVERRIDE;
-
- const svalue *get_arg () const { return m_arg; }
-
- private:
- const svalue *m_arg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <const unmergeable_svalue *>::test (const svalue *sval)
-{
- return sval->get_kind () == SK_UNMERGEABLE;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue for use in selftests, where
- we want a specific but unknown svalue.
- Unlike other svalue subclasses these aren't managed by
- region_model_manager. */
-
-class placeholder_svalue : public svalue
-{
-public:
- placeholder_svalue (tree type, const char *name)
- : svalue (complexity (1, 1), type), m_name (name)
- {
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_PLACEHOLDER; }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
-
- const char *get_name () const { return m_name; }
-
- private:
- const char *m_name;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <placeholder_svalue *>::test (svalue *sval)
-{
- return sval->get_kind () == SK_PLACEHOLDER;
-}
-
-namespace ana {
-
-/* Concrete subclass of svalue representing a "widening" seen when merging
- states, widening from a base value to {base value, iter value} and thus
- representing a possible fixed point in an iteration from the base to
- +ve infinity, or -ve infinity, and thus useful for representing a value
- within a loop.
- We also need to capture the program_point at which the merger happens,
- so that distinguish between different iterators, and thus handle
- nested loops. (currently we capture the function_point instead, for
- simplicity of hashing). */
-
-class widening_svalue : public svalue
-{
-public:
- /* A support class for uniquifying instances of widening_svalue. */
- struct key_t
- {
- key_t (tree type, const program_point &point,
- const svalue *base_sval, const svalue *iter_sval)
- : m_type (type), m_point (point.get_function_point ()),
- m_base_sval (base_sval), m_iter_sval (iter_sval)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- hstate.add_ptr (m_base_sval);
- hstate.add_ptr (m_iter_sval);
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_type == other.m_type
- && m_point == other.m_point
- && m_base_sval == other.m_base_sval
- && m_iter_sval == other.m_iter_sval);
- }
-
- 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; }
-
- tree m_type;
- function_point m_point;
- const svalue *m_base_sval;
- const svalue *m_iter_sval;
- };
-
- enum direction_t
- {
- DIR_ASCENDING,
- DIR_DESCENDING,
- DIR_UNKNOWN
- };
-
- widening_svalue (tree type, const program_point &point,
- const svalue *base_sval, const svalue *iter_sval)
- : svalue (complexity::from_pair (base_sval->get_complexity (),
- iter_sval->get_complexity ()),
- type),
- m_point (point.get_function_point ()),
- m_base_sval (base_sval), m_iter_sval (iter_sval)
- {
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_WIDENING; }
- const widening_svalue *dyn_cast_widening_svalue () const FINAL OVERRIDE
- {
- return this;
- }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
-
- const function_point &get_point () const { return m_point; }
- const svalue *get_base_svalue () const { return m_base_sval; }
- const svalue *get_iter_svalue () const { return m_iter_sval; }
-
- enum direction_t get_direction () const;
-
- tristate eval_condition_without_cm (enum tree_code op,
- tree rhs_cst) const;
-
- private:
- function_point m_point;
- const svalue *m_base_sval;
- const svalue *m_iter_sval;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <widening_svalue *>::test (svalue *sval)
-{
- return sval->get_kind () == SK_WIDENING;
-}
-
-template <> struct default_hash_traits<widening_svalue::key_t>
-: public member_function_hash_traits<widening_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* Concrete subclass of svalue representing a mapping of bit-ranges
- to svalues, analogous to a cluster within the store.
-
- This is for use in places where we want to represent a store-like
- mapping, but are required to use an svalue, such as when handling
- compound assignments and compound return values.
-
- All keys within the underlying binding_map are required to be concrete,
- not symbolic.
-
- Instances of this class shouldn't be bound as-is into the store;
- instead they should be unpacked. Similarly, they should not be
- nested. */
-
-class compound_svalue : public svalue
-{
-public:
- typedef binding_map::iterator_t iterator_t;
-
- /* A support class for uniquifying instances of compound_svalue.
- Note that to avoid copies, keys store pointers to binding_maps,
- rather than the maps themselves. */
- struct key_t
- {
- key_t (tree type, const binding_map *map_ptr)
- : m_type (type), m_map_ptr (map_ptr)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- hstate.add_ptr (m_type);
- //hstate.add_ptr (m_map_ptr); // TODO
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_type == other.m_type
- && *m_map_ptr == *other.m_map_ptr);
- }
-
- 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; }
-
- tree m_type;
- const binding_map *m_map_ptr;
- };
-
- compound_svalue (tree type, const binding_map &map);
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_COMPOUND; }
- const compound_svalue *dyn_cast_compound_svalue () const FINAL OVERRIDE
- {
- return this;
- }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
-
- const binding_map &get_map () const { return m_map; }
-
- iterator_t begin () const { return m_map.begin (); }
- iterator_t end () const { return m_map.end (); }
-
- struct key_t make_key () const
- {
- return key_t (get_type (), &m_map);
- }
-
- private:
- static complexity calc_complexity (const binding_map &map);
-
- binding_map m_map;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <compound_svalue *>::test (svalue *sval)
-{
- return sval->get_kind () == SK_COMPOUND;
-}
-
-template <> struct default_hash_traits<compound_svalue::key_t>
-: public member_function_hash_traits<compound_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* A defined value arising from a statement, where we want to identify a
- particular unknown value, rather than resorting to the unknown_value
- singleton, so that the value can have sm-state.
-
- Comparisons of variables that share the same conjured_svalue are known
- to be equal, even if we don't know what the value is.
-
- For example, this is used for the values of regions that may have been
- touched when calling an unknown function.
-
- The value captures a region as well as a stmt in order to avoid falsely
- aliasing the various values that could arise in one statement. For
- example, after:
- unknown_fn (&a, &b);
- we want values to clobber a and b with, but we don't want to use the
- same value, or it would falsely implicitly assume that a == b. */
-
-class conjured_svalue : public svalue
-{
-public:
- typedef binding_map::iterator_t iterator_t;
-
- /* A support class for uniquifying instances of conjured_svalue. */
- struct key_t
- {
- key_t (tree type, const gimple *stmt, const region *id_reg)
- : m_type (type), m_stmt (stmt), m_id_reg (id_reg)
- {}
-
- hashval_t hash () const
- {
- inchash::hash hstate;
- hstate.add_ptr (m_type);
- hstate.add_ptr (m_stmt);
- hstate.add_ptr (m_id_reg);
- return hstate.end ();
- }
-
- bool operator== (const key_t &other) const
- {
- return (m_type == other.m_type
- && m_stmt == other.m_stmt
- && m_id_reg == other.m_id_reg);
- }
-
- /* Use m_stmt to mark empty/deleted, as m_type can be NULL for
- legitimate instances. */
- void mark_deleted () { m_stmt = reinterpret_cast<const gimple *> (1); }
- void mark_empty () { m_stmt = NULL; }
- bool is_deleted () const
- {
- return m_stmt == reinterpret_cast<const gimple *> (1);
- }
- bool is_empty () const { return m_stmt == NULL; }
-
- tree m_type;
- const gimple *m_stmt;
- const region *m_id_reg;
- };
-
- conjured_svalue (tree type, const gimple *stmt, const region *id_reg)
- : svalue (complexity (id_reg), type),
- m_stmt (stmt), m_id_reg (id_reg)
- {
- gcc_assert (m_stmt != NULL);
- }
-
- enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_CONJURED; }
- const conjured_svalue *dyn_cast_conjured_svalue () const FINAL OVERRIDE
- {
- return this;
- }
-
- void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
- void accept (visitor *v) const FINAL OVERRIDE;
-
- const gimple *get_stmt () const { return m_stmt; }
- const region *get_id_region () const { return m_id_reg; }
-
- private:
- const gimple *m_stmt;
- const region *m_id_reg;
-};
-
-} // namespace ana
-
-template <>
-template <>
-inline bool
-is_a_helper <conjured_svalue *>::test (svalue *sval)
-{
- return sval->get_kind () == SK_CONJURED;
-}
-
-template <> struct default_hash_traits<conjured_svalue::key_t>
-: public member_function_hash_traits<conjured_svalue::key_t>
-{
- static const bool empty_zero_p = true;
-};
-
-namespace ana {
-
-/* 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_CAST,
- RK_HEAP_ALLOCATED,
- RK_ALLOCA,
- RK_STRING,
- 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)
- cast_region (RK_CAST)
- heap_allocated_region (RK_HEAP_ALLOCATED)
- alloca_region (RK_ALLOCA)
- string_region (RK_STRING)
- 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 cast_region *
- dyn_cast_cast_region () const { return NULL; }
- virtual const string_region *
- dyn_cast_string_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;
-
- 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;
-
- void dump_to_pp (const region_model &model,
- pretty_printer *pp,
- const char *prefix,
- bool is_last_child) 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 *);
-
- region_offset get_offset () const;
- bool get_byte_size (byte_size_t *out) const;
- bool get_bit_size (bit_size_t *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;
-
- const complexity &get_complexity () const { return m_complexity; }
-
- 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; }
- 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;
-
- unsigned get_num_locals () const { return m_locals.elements (); }
-
- 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)
- : region (complexity::from_pair (parent, sval_ptr), id, parent,
- TREE_TYPE (sval_ptr->get_type ())),
- m_sval_ptr (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). */
-
-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)
- {}
-
- 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;
-
- 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:
- tree m_decl;
-};
-
-} // 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; }
-
-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; }
-
-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; }
-
-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 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 {
-
-/* 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;
-};
-
/* A class responsible for owning and consolidating region and svalue
instances.
region and svalue instances are immutable as far as clients are
#include "analyzer/call-string.h"
#include "analyzer/program-point.h"
#include "analyzer/store.h"
+#include "analyzer/region.h"
#include "analyzer/region-model.h"
#if ENABLE_ANALYZER
/* class symbolic_region : public map_region. */
+/* symbolic_region's ctor. */
+
+symbolic_region::symbolic_region (unsigned id, region *parent,
+ const svalue *sval_ptr)
+: region (complexity::from_pair (parent, sval_ptr), id, parent,
+ TREE_TYPE (sval_ptr->get_type ())),
+ m_sval_ptr (sval_ptr)
+{
+}
+
/* Implementation of region::accept vfunc for symbolic_region. */
void
--- /dev/null
+/* Regions of memory.
+ Copyright (C) 2019-2020 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 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_CAST,
+ RK_HEAP_ALLOCATED,
+ RK_ALLOCA,
+ RK_STRING,
+ 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)
+ cast_region (RK_CAST)
+ heap_allocated_region (RK_HEAP_ALLOCATED)
+ alloca_region (RK_ALLOCA)
+ string_region (RK_STRING)
+ 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 cast_region *
+ dyn_cast_cast_region () const { return NULL; }
+ virtual const string_region *
+ dyn_cast_string_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;
+
+ 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;
+
+ void dump_to_pp (const region_model &model,
+ pretty_printer *pp,
+ const char *prefix,
+ bool is_last_child) 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 *);
+
+ region_offset get_offset () const;
+ bool get_byte_size (byte_size_t *out) const;
+ bool get_bit_size (bit_size_t *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;
+
+ const complexity &get_complexity () const { return m_complexity; }
+
+ 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; }
+ 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;
+
+ unsigned get_num_locals () const { return m_locals.elements (); }
+
+ 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). */
+
+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)
+ {}
+
+ 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;
+
+ 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:
+ tree m_decl;
+};
+
+} // 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; }
+
+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; }
+
+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; }
+
+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 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 {
+
+/* 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 */
#include "analyzer/call-string.h"
#include "analyzer/program-point.h"
#include "analyzer/store.h"
+#include "analyzer/svalue.h"
#include "analyzer/region-model.h"
#if ENABLE_ANALYZER
namespace ana {
-/* struct complexity. */
-
-/* Get complexity for a new node that references REG
- (the complexity of REG, plus one for the new node). */
-
-complexity::complexity (const region *reg)
-: m_num_nodes (reg->get_complexity ().m_num_nodes + 1),
- m_max_depth (reg->get_complexity ().m_max_depth + 1)
-{
-}
-
-/* Get complexity for a new node that references SVAL.
- (the complexity of SVAL, plus one for the new node). */
-
-complexity::complexity (const svalue *sval)
-: m_num_nodes (sval->get_complexity ().m_num_nodes + 1),
- m_max_depth (sval->get_complexity ().m_max_depth + 1)
-{
-}
-
-/* Get complexity for a new node that references nodes with complexity
- C1 and C2. */
-
-complexity
-complexity::from_pair (const complexity &c1, const complexity &c2)
-{
- return complexity (c1.m_num_nodes + c2.m_num_nodes + 1,
- MAX (c1.m_max_depth, c2.m_max_depth) + 1);
-}
-
/* class svalue and its various subclasses. */
/* class svalue. */
--- /dev/null
+/* Symbolic values.
+ Copyright (C) 2019-2020 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_SVALUE_H
+#define GCC_ANALYZER_SVALUE_H
+
+#include "analyzer/complexity.h"
+
+using namespace ana;
+
+namespace ana {
+
+/* An enum for discriminating between the different concrete subclasses
+ of svalue. */
+
+enum svalue_kind
+{
+ SK_REGION,
+ SK_CONSTANT,
+ SK_UNKNOWN,
+ SK_POISONED,
+ SK_SETJMP,
+ SK_INITIAL,
+ SK_UNARYOP,
+ SK_BINOP,
+ SK_SUB,
+ SK_UNMERGEABLE,
+ SK_PLACEHOLDER,
+ SK_WIDENING,
+ SK_COMPOUND,
+ SK_CONJURED
+};
+
+/* svalue and its subclasses.
+
+ The class hierarchy looks like this (using indentation to show
+ inheritance, and with svalue_kinds shown for the concrete subclasses):
+
+ svalue
+ region_svalue (SK_REGION): a pointer to a region
+ constant_svalue (SK_CONSTANT): a constant
+ unknown_svalue (SK_UNKNOWN): an unknowable value
+ poisoned_svalue (SK_POISONED): a unusable value (undefined)
+ setjmp_svalue (SK_SETJMP): a setjmp/longjmp buffer
+ initial_svalue (SK_INITIAL): the initial value of a region
+ unaryop_svalue (SK_UNARYOP): unary operation on another svalue
+ binop_svalue (SK_BINOP): binary operation on two svalues
+ sub_svalue (SK_SUB): the result of accessing a subregion
+ unmergeable_svalue (SK_UNMERGEABLE): a value that is so interesting
+ from a control-flow perspective that it can inhibit state-merging
+ placeholder_svalue (SK_PLACEHOLDER): for use in selftests.
+ widening_svalue (SK_WIDENING): a merger of two svalues (possibly
+ in an iteration).
+ compound_svalue (SK_COMPOUND): a mapping of bit-ranges to svalues
+ conjured_svalue (SK_CONJURED): a value arising from a stmt. */
+
+/* An abstract base class representing a value held by a region of memory. */
+
+class svalue
+{
+public:
+ virtual ~svalue () {}
+
+ tree get_type () const { return m_type; }
+
+ virtual enum svalue_kind get_kind () const = 0;
+
+ void print (const region_model &model,
+ pretty_printer *pp) const;
+
+ virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
+ void dump (bool simple=true) const;
+ label_text get_desc (bool simple=true) const;
+
+ json::value *to_json () const;
+
+ virtual const region_svalue *
+ dyn_cast_region_svalue () const { return NULL; }
+ virtual const constant_svalue *
+ dyn_cast_constant_svalue () const { return NULL; }
+ virtual const poisoned_svalue *
+ dyn_cast_poisoned_svalue () const { return NULL; }
+ virtual const setjmp_svalue *
+ dyn_cast_setjmp_svalue () const { return NULL; }
+ virtual const initial_svalue *
+ dyn_cast_initial_svalue () const { return NULL; }
+ virtual const unaryop_svalue *
+ dyn_cast_unaryop_svalue () const { return NULL; }
+ virtual const binop_svalue *
+ dyn_cast_binop_svalue () const { return NULL; }
+ virtual const sub_svalue *
+ dyn_cast_sub_svalue () const { return NULL; }
+ virtual const unmergeable_svalue *
+ dyn_cast_unmergeable_svalue () const { return NULL; }
+ virtual const widening_svalue *
+ dyn_cast_widening_svalue () const { return NULL; }
+ virtual const compound_svalue *
+ dyn_cast_compound_svalue () const { return NULL; }
+ virtual const conjured_svalue *
+ dyn_cast_conjured_svalue () const { return NULL; }
+
+ tree maybe_get_constant () const;
+ const svalue *maybe_undo_cast () const;
+ const svalue *unwrap_any_unmergeable () const;
+
+ const svalue *can_merge_p (const svalue *other,
+ region_model_manager *mgr,
+ model_merger *merger) const;
+
+ const complexity &get_complexity () const { return m_complexity; }
+
+ virtual void accept (visitor *v) const = 0;
+
+ bool live_p (const svalue_set &live_svalues,
+ const region_model *model) const;
+ virtual bool implicitly_live_p (const svalue_set &live_svalues,
+ const region_model *model) const;
+
+ static int cmp_ptr (const svalue *, const svalue *);
+ static int cmp_ptr_ptr (const void *, const void *);
+
+ protected:
+ svalue (complexity c, tree type)
+ : m_complexity (c), m_type (type)
+ {}
+
+ private:
+ complexity m_complexity;
+ tree m_type;
+};
+
+/* Concrete subclass of svalue representing a pointer value that points to
+ a known region */
+
+class region_svalue : public svalue
+{
+public:
+ /* A support class for uniquifying instances of region_svalue. */
+ struct key_t
+ {
+ key_t (tree type, const region *reg)
+ : m_type (type), m_reg (reg)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_ptr (m_type);
+ hstate.add_ptr (m_reg);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_type == other.m_type && m_reg == other.m_reg);
+ }
+
+ 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; }
+
+ tree m_type;
+ const region *m_reg;
+ };
+
+ region_svalue (tree type, const region *reg)
+ : svalue (complexity (reg), type),
+ m_reg (reg)
+ {
+ gcc_assert (m_reg != NULL);
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_REGION; }
+ const region_svalue *
+ dyn_cast_region_svalue () const FINAL OVERRIDE { return this; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+
+ const region * get_pointee () const { return m_reg; }
+
+ static tristate eval_condition (const region_svalue *lhs_ptr,
+ enum tree_code op,
+ const region_svalue *rhs_ptr);
+
+ private:
+ const region *m_reg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const region_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_REGION;
+}
+
+template <> struct default_hash_traits<region_svalue::key_t>
+: public member_function_hash_traits<region_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue representing a specific constant value. */
+
+class constant_svalue : public svalue
+{
+public:
+ constant_svalue (tree cst_expr)
+ : svalue (complexity (1, 1), TREE_TYPE (cst_expr)), m_cst_expr (cst_expr)
+ {
+ gcc_assert (cst_expr);
+ gcc_assert (CONSTANT_CLASS_P (cst_expr));
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_CONSTANT; }
+ const constant_svalue *
+ dyn_cast_constant_svalue () const FINAL OVERRIDE { return this; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+ bool implicitly_live_p (const svalue_set &,
+ const region_model *) const FINAL OVERRIDE;
+
+ tree get_constant () const { return m_cst_expr; }
+ static tristate eval_condition (const constant_svalue *lhs,
+ enum tree_code op,
+ const constant_svalue *rhs);
+
+ private:
+ tree m_cst_expr;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const constant_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_CONSTANT;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue representing an unknowable value, the bottom
+ value when thinking of svalues as a lattice.
+ This is a singleton (w.r.t. its manager): there is a single unknown_svalue
+ per type. Self-comparisons of such instances yield "unknown". */
+
+class unknown_svalue : public svalue
+{
+public:
+ unknown_svalue (tree type)
+ : svalue (complexity (1, 1), type)
+ {}
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_UNKNOWN; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+};
+
+/* An enum describing a particular kind of "poisoned" value. */
+
+enum poison_kind
+{
+ /* For use to describe freed memory. */
+ POISON_KIND_FREED,
+
+ /* For use on pointers to regions within popped stack frames. */
+ POISON_KIND_POPPED_STACK
+};
+
+extern const char *poison_kind_to_str (enum poison_kind);
+
+/* Concrete subclass of svalue representing a value that should not
+ be used (e.g. uninitialized memory, freed memory). */
+
+class poisoned_svalue : public svalue
+{
+public:
+ /* A support class for uniquifying instances of poisoned_svalue. */
+ struct key_t
+ {
+ key_t (enum poison_kind kind, tree type)
+ : m_kind (kind), m_type (type)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_int (m_kind);
+ hstate.add_ptr (m_type);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_kind == other.m_kind && 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; }
+
+ enum poison_kind m_kind;
+ tree m_type;
+ };
+
+ poisoned_svalue (enum poison_kind kind, tree type)
+ : svalue (complexity (1, 1), type), m_kind (kind) {}
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_POISONED; }
+ const poisoned_svalue *
+ dyn_cast_poisoned_svalue () const FINAL OVERRIDE { return this; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+
+ enum poison_kind get_poison_kind () const { return m_kind; }
+
+ private:
+ enum poison_kind m_kind;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const poisoned_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_POISONED;
+}
+
+template <> struct default_hash_traits<poisoned_svalue::key_t>
+: public member_function_hash_traits<poisoned_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* A bundle of information recording a setjmp/sigsetjmp call, corresponding
+ roughly to a jmp_buf. */
+
+struct setjmp_record
+{
+ setjmp_record (const exploded_node *enode,
+ const gcall *setjmp_call)
+ : m_enode (enode), m_setjmp_call (setjmp_call)
+ {
+ }
+
+ bool operator== (const setjmp_record &other) const
+ {
+ return (m_enode == other.m_enode
+ && m_setjmp_call == other.m_setjmp_call);
+ }
+
+ void add_to_hash (inchash::hash *hstate) const
+ {
+ hstate->add_ptr (m_enode);
+ hstate->add_ptr (m_setjmp_call);
+ }
+
+ static int cmp (const setjmp_record &rec1, const setjmp_record &rec2);
+
+ const exploded_node *m_enode;
+ const gcall *m_setjmp_call;
+};
+
+/* Concrete subclass of svalue representing buffers for setjmp/sigsetjmp,
+ so that longjmp/siglongjmp can potentially "return" to an entirely
+ different function. */
+
+class setjmp_svalue : public svalue
+{
+public:
+ /* A support class for uniquifying instances of poisoned_svalue. */
+ struct key_t
+ {
+ key_t (const setjmp_record &record, tree type)
+ : m_record (record), m_type (type)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ m_record.add_to_hash (&hstate);
+ hstate.add_ptr (m_type);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_record == other.m_record && 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; }
+
+ setjmp_record m_record;
+ tree m_type;
+ };
+
+ setjmp_svalue (const setjmp_record &setjmp_record,
+ tree type)
+ : svalue (complexity (1, 1), type), m_setjmp_record (setjmp_record)
+ {}
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_SETJMP; }
+ const setjmp_svalue *
+ dyn_cast_setjmp_svalue () const FINAL OVERRIDE { return this; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+
+ int get_enode_index () const;
+
+ const setjmp_record &get_setjmp_record () const { return m_setjmp_record; }
+
+ private:
+ setjmp_record m_setjmp_record;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const setjmp_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_SETJMP;
+}
+
+template <> struct default_hash_traits<setjmp_svalue::key_t>
+: public member_function_hash_traits<setjmp_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue representing the initial value of a
+ specific region.
+
+ This represents the initial value at the start of the analysis path,
+ as opposed to the first time the region is accessed during the path.
+ Hence as soon as we have a call to an unknown function, all previously
+ unmodelled globals become implicitly "unknown" rathen than "initial". */
+
+class initial_svalue : public svalue
+{
+public:
+ initial_svalue (tree type, const region *reg)
+ : svalue (complexity (reg), type), m_reg (reg)
+ {
+ gcc_assert (m_reg != NULL);
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_INITIAL; }
+ const initial_svalue *
+ dyn_cast_initial_svalue () const FINAL OVERRIDE { return this; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+ bool implicitly_live_p (const svalue_set &,
+ const region_model *) const FINAL OVERRIDE;
+
+ const region *get_region () const { return m_reg; }
+
+ private:
+ const region *m_reg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const initial_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_INITIAL;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue representing a unary operation on
+ another svalues (e.g. a cast). */
+
+class unaryop_svalue : public svalue
+{
+public:
+ /* A support class for uniquifying instances of unaryop_svalue. */
+ struct key_t
+ {
+ key_t (tree type, enum tree_code op, const svalue *arg)
+ : m_type (type), m_op (op), m_arg (arg)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_ptr (m_type);
+ hstate.add_int (m_op);
+ hstate.add_ptr (m_arg);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_type == other.m_type
+ && m_op == other.m_op
+ && m_arg == other.m_arg);
+ }
+
+ 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; }
+
+ tree m_type;
+ enum tree_code m_op;
+ const svalue *m_arg;
+ };
+
+ unaryop_svalue (tree type, enum tree_code op, const svalue *arg)
+ : svalue (complexity (arg), type), m_op (op), m_arg (arg)
+ {
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_UNARYOP; }
+ const unaryop_svalue *
+ dyn_cast_unaryop_svalue () const FINAL OVERRIDE { return this; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+ bool implicitly_live_p (const svalue_set &,
+ const region_model *) const FINAL OVERRIDE;
+
+ enum tree_code get_op () const { return m_op; }
+ const svalue *get_arg () const { return m_arg; }
+
+ private:
+ enum tree_code m_op;
+ const svalue *m_arg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const unaryop_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_UNARYOP;
+}
+
+template <> struct default_hash_traits<unaryop_svalue::key_t>
+: public member_function_hash_traits<unaryop_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue representing a binary operation of
+ two svalues. */
+
+class binop_svalue : public svalue
+{
+public:
+ /* A support class for uniquifying instances of binop_svalue. */
+ struct key_t
+ {
+ key_t (tree type, enum tree_code op,
+ const svalue *arg0, const svalue *arg1)
+ : m_type (type), m_op (op), m_arg0 (arg0), m_arg1 (arg1)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_ptr (m_type);
+ hstate.add_int (m_op);
+ hstate.add_ptr (m_arg0);
+ hstate.add_ptr (m_arg1);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_type == other.m_type
+ && m_op == other.m_op
+ && m_arg0 == other.m_arg0
+ && m_arg1 == other.m_arg1);
+ }
+
+ 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; }
+
+ tree m_type;
+ enum tree_code m_op;
+ const svalue *m_arg0;
+ const svalue *m_arg1;
+ };
+
+ binop_svalue (tree type, enum tree_code op,
+ const svalue *arg0, const svalue *arg1)
+ : svalue (complexity::from_pair (arg0->get_complexity (),
+ arg1->get_complexity ()),
+ type),
+ m_op (op), m_arg0 (arg0), m_arg1 (arg1)
+ {
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_BINOP; }
+ const binop_svalue *dyn_cast_binop_svalue () const FINAL OVERRIDE
+ {
+ return this;
+ }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+ bool implicitly_live_p (const svalue_set &,
+ const region_model *) const FINAL OVERRIDE;
+
+ enum tree_code get_op () const { return m_op; }
+ const svalue *get_arg0 () const { return m_arg0; }
+ const svalue *get_arg1 () const { return m_arg1; }
+
+ private:
+ enum tree_code m_op;
+ const svalue *m_arg0;
+ const svalue *m_arg1;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const binop_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_BINOP;
+}
+
+template <> struct default_hash_traits<binop_svalue::key_t>
+: public member_function_hash_traits<binop_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue representing the result of accessing a subregion
+ of another svalue (the value of a component/field of a struct, or an element
+ from an array). */
+
+class sub_svalue : public svalue
+{
+public:
+ /* A support class for uniquifying instances of sub_svalue. */
+ struct key_t
+ {
+ key_t (tree type, const svalue *parent_svalue, const region *subregion)
+ : m_type (type), m_parent_svalue (parent_svalue), m_subregion (subregion)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_ptr (m_type);
+ hstate.add_ptr (m_parent_svalue);
+ hstate.add_ptr (m_subregion);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_type == other.m_type
+ && m_parent_svalue == other.m_parent_svalue
+ && m_subregion == other.m_subregion);
+ }
+
+ 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; }
+
+ tree m_type;
+ const svalue *m_parent_svalue;
+ const region *m_subregion;
+ };
+ sub_svalue (tree type, const svalue *parent_svalue,
+ const region *subregion);
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_SUB; }
+ const sub_svalue *dyn_cast_sub_svalue () const FINAL OVERRIDE
+ {
+ return this;
+ }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+ bool implicitly_live_p (const svalue_set &,
+ const region_model *) const FINAL OVERRIDE;
+
+ const svalue *get_parent () const { return m_parent_svalue; }
+ const region *get_subregion () const { return m_subregion; }
+
+ private:
+ const svalue *m_parent_svalue;
+ const region *m_subregion;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const sub_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_SUB;
+}
+
+template <> struct default_hash_traits<sub_svalue::key_t>
+: public member_function_hash_traits<sub_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue: decorate another svalue,
+ so that the resulting svalue can be identified as being
+ "interesting to control flow".
+ For example, consider the return value from setjmp. We
+ don't want to merge states in which the result is 0 with
+ those in which the result is non-zero. By using an
+ unmergeable_svalue for the result, we can inhibit such merges
+ and have separate exploded nodes for those states, keeping
+ the first and second returns from setjmp distinct in the exploded
+ graph. */
+
+class unmergeable_svalue : public svalue
+{
+public:
+ unmergeable_svalue (const svalue *arg)
+ : svalue (complexity (arg), arg->get_type ()), m_arg (arg)
+ {
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_UNMERGEABLE; }
+ const unmergeable_svalue *
+ dyn_cast_unmergeable_svalue () const FINAL OVERRIDE { return this; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+ bool implicitly_live_p (const svalue_set &,
+ const region_model *) const FINAL OVERRIDE;
+
+ const svalue *get_arg () const { return m_arg; }
+
+ private:
+ const svalue *m_arg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const unmergeable_svalue *>::test (const svalue *sval)
+{
+ return sval->get_kind () == SK_UNMERGEABLE;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue for use in selftests, where
+ we want a specific but unknown svalue.
+ Unlike other svalue subclasses these aren't managed by
+ region_model_manager. */
+
+class placeholder_svalue : public svalue
+{
+public:
+ placeholder_svalue (tree type, const char *name)
+ : svalue (complexity (1, 1), type), m_name (name)
+ {
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_PLACEHOLDER; }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+
+ const char *get_name () const { return m_name; }
+
+ private:
+ const char *m_name;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <placeholder_svalue *>::test (svalue *sval)
+{
+ return sval->get_kind () == SK_PLACEHOLDER;
+}
+
+namespace ana {
+
+/* Concrete subclass of svalue representing a "widening" seen when merging
+ states, widening from a base value to {base value, iter value} and thus
+ representing a possible fixed point in an iteration from the base to
+ +ve infinity, or -ve infinity, and thus useful for representing a value
+ within a loop.
+ We also need to capture the program_point at which the merger happens,
+ so that distinguish between different iterators, and thus handle
+ nested loops. (currently we capture the function_point instead, for
+ simplicity of hashing). */
+
+class widening_svalue : public svalue
+{
+public:
+ /* A support class for uniquifying instances of widening_svalue. */
+ struct key_t
+ {
+ key_t (tree type, const program_point &point,
+ const svalue *base_sval, const svalue *iter_sval)
+ : m_type (type), m_point (point.get_function_point ()),
+ m_base_sval (base_sval), m_iter_sval (iter_sval)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_ptr (m_base_sval);
+ hstate.add_ptr (m_iter_sval);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_type == other.m_type
+ && m_point == other.m_point
+ && m_base_sval == other.m_base_sval
+ && m_iter_sval == other.m_iter_sval);
+ }
+
+ 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; }
+
+ tree m_type;
+ function_point m_point;
+ const svalue *m_base_sval;
+ const svalue *m_iter_sval;
+ };
+
+ enum direction_t
+ {
+ DIR_ASCENDING,
+ DIR_DESCENDING,
+ DIR_UNKNOWN
+ };
+
+ widening_svalue (tree type, const program_point &point,
+ const svalue *base_sval, const svalue *iter_sval)
+ : svalue (complexity::from_pair (base_sval->get_complexity (),
+ iter_sval->get_complexity ()),
+ type),
+ m_point (point.get_function_point ()),
+ m_base_sval (base_sval), m_iter_sval (iter_sval)
+ {
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_WIDENING; }
+ const widening_svalue *dyn_cast_widening_svalue () const FINAL OVERRIDE
+ {
+ return this;
+ }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+
+ const function_point &get_point () const { return m_point; }
+ const svalue *get_base_svalue () const { return m_base_sval; }
+ const svalue *get_iter_svalue () const { return m_iter_sval; }
+
+ enum direction_t get_direction () const;
+
+ tristate eval_condition_without_cm (enum tree_code op,
+ tree rhs_cst) const;
+
+ private:
+ function_point m_point;
+ const svalue *m_base_sval;
+ const svalue *m_iter_sval;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <widening_svalue *>::test (svalue *sval)
+{
+ return sval->get_kind () == SK_WIDENING;
+}
+
+template <> struct default_hash_traits<widening_svalue::key_t>
+: public member_function_hash_traits<widening_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* Concrete subclass of svalue representing a mapping of bit-ranges
+ to svalues, analogous to a cluster within the store.
+
+ This is for use in places where we want to represent a store-like
+ mapping, but are required to use an svalue, such as when handling
+ compound assignments and compound return values.
+
+ All keys within the underlying binding_map are required to be concrete,
+ not symbolic.
+
+ Instances of this class shouldn't be bound as-is into the store;
+ instead they should be unpacked. Similarly, they should not be
+ nested. */
+
+class compound_svalue : public svalue
+{
+public:
+ typedef binding_map::iterator_t iterator_t;
+
+ /* A support class for uniquifying instances of compound_svalue.
+ Note that to avoid copies, keys store pointers to binding_maps,
+ rather than the maps themselves. */
+ struct key_t
+ {
+ key_t (tree type, const binding_map *map_ptr)
+ : m_type (type), m_map_ptr (map_ptr)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_ptr (m_type);
+ //hstate.add_ptr (m_map_ptr); // TODO
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_type == other.m_type
+ && *m_map_ptr == *other.m_map_ptr);
+ }
+
+ 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; }
+
+ tree m_type;
+ const binding_map *m_map_ptr;
+ };
+
+ compound_svalue (tree type, const binding_map &map);
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_COMPOUND; }
+ const compound_svalue *dyn_cast_compound_svalue () const FINAL OVERRIDE
+ {
+ return this;
+ }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+
+ const binding_map &get_map () const { return m_map; }
+
+ iterator_t begin () const { return m_map.begin (); }
+ iterator_t end () const { return m_map.end (); }
+
+ struct key_t make_key () const
+ {
+ return key_t (get_type (), &m_map);
+ }
+
+ private:
+ static complexity calc_complexity (const binding_map &map);
+
+ binding_map m_map;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <compound_svalue *>::test (svalue *sval)
+{
+ return sval->get_kind () == SK_COMPOUND;
+}
+
+template <> struct default_hash_traits<compound_svalue::key_t>
+: public member_function_hash_traits<compound_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+namespace ana {
+
+/* A defined value arising from a statement, where we want to identify a
+ particular unknown value, rather than resorting to the unknown_value
+ singleton, so that the value can have sm-state.
+
+ Comparisons of variables that share the same conjured_svalue are known
+ to be equal, even if we don't know what the value is.
+
+ For example, this is used for the values of regions that may have been
+ touched when calling an unknown function.
+
+ The value captures a region as well as a stmt in order to avoid falsely
+ aliasing the various values that could arise in one statement. For
+ example, after:
+ unknown_fn (&a, &b);
+ we want values to clobber a and b with, but we don't want to use the
+ same value, or it would falsely implicitly assume that a == b. */
+
+class conjured_svalue : public svalue
+{
+public:
+ typedef binding_map::iterator_t iterator_t;
+
+ /* A support class for uniquifying instances of conjured_svalue. */
+ struct key_t
+ {
+ key_t (tree type, const gimple *stmt, const region *id_reg)
+ : m_type (type), m_stmt (stmt), m_id_reg (id_reg)
+ {}
+
+ hashval_t hash () const
+ {
+ inchash::hash hstate;
+ hstate.add_ptr (m_type);
+ hstate.add_ptr (m_stmt);
+ hstate.add_ptr (m_id_reg);
+ return hstate.end ();
+ }
+
+ bool operator== (const key_t &other) const
+ {
+ return (m_type == other.m_type
+ && m_stmt == other.m_stmt
+ && m_id_reg == other.m_id_reg);
+ }
+
+ /* Use m_stmt to mark empty/deleted, as m_type can be NULL for
+ legitimate instances. */
+ void mark_deleted () { m_stmt = reinterpret_cast<const gimple *> (1); }
+ void mark_empty () { m_stmt = NULL; }
+ bool is_deleted () const
+ {
+ return m_stmt == reinterpret_cast<const gimple *> (1);
+ }
+ bool is_empty () const { return m_stmt == NULL; }
+
+ tree m_type;
+ const gimple *m_stmt;
+ const region *m_id_reg;
+ };
+
+ conjured_svalue (tree type, const gimple *stmt, const region *id_reg)
+ : svalue (complexity (id_reg), type),
+ m_stmt (stmt), m_id_reg (id_reg)
+ {
+ gcc_assert (m_stmt != NULL);
+ }
+
+ enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_CONJURED; }
+ const conjured_svalue *dyn_cast_conjured_svalue () const FINAL OVERRIDE
+ {
+ return this;
+ }
+
+ void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+ void accept (visitor *v) const FINAL OVERRIDE;
+
+ const gimple *get_stmt () const { return m_stmt; }
+ const region *get_id_region () const { return m_id_reg; }
+
+ private:
+ const gimple *m_stmt;
+ const region *m_id_reg;
+};
+
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <conjured_svalue *>::test (svalue *sval)
+{
+ return sval->get_kind () == SK_CONJURED;
+}
+
+template <> struct default_hash_traits<conjured_svalue::key_t>
+: public member_function_hash_traits<conjured_svalue::key_t>
+{
+ static const bool empty_zero_p = true;
+};
+
+#endif /* GCC_ANALYZER_SVALUE_H */