aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/svalue.h
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2020-10-28 20:09:04 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2020-10-28 20:09:04 -0400
commite9751143e237b507a81234a573a200ea45e7111a (patch)
tree67c5ae2a3112bd02c8b4aa98f8287251481a3e3a /gcc/analyzer/svalue.h
parent0a36f5f21cd9dcaaf99e78d2ec995d6cb2918274 (diff)
downloadgcc-e9751143e237b507a81234a573a200ea45e7111a.zip
gcc-e9751143e237b507a81234a573a200ea45e7111a.tar.gz
gcc-e9751143e237b507a81234a573a200ea45e7111a.tar.bz2
analyzer: move svalue and region decls to their own header files
gcc/ChangeLog: * Makefile.in (ANALYZER_OBJS): Add analyzer/complexity.o. gcc/analyzer/ChangeLog: * analyzer.h (class state_machine): New forward decl. (class logger): Likewise. (class visitor): Likewise. * complexity.cc: New file, taken from svalue.cc. * complexity.h: New file, taken from region-model.h. * region-model.h: Include "analyzer/svalue.h" and "analyzer/region.h". Move struct complexity to complexity.h. Move svalue, its subclasses and supporting decls to svalue.h. Move region, its subclasses and supporting decls to region.h. * region.cc: Include "analyzer/region.h". (symbolic_region::symbolic_region): Move here from region-model.h. * region.h: New file, based on material from region-model.h. * svalue.cc: Include "analyzer/svalue.h". (complexity::complexity): Move to complexity.cc. (complexity::from_pair): Likewise. * svalue.h: New file, based on material from region-model.h.
Diffstat (limited to 'gcc/analyzer/svalue.h')
-rw-r--r--gcc/analyzer/svalue.h1150
1 files changed, 1150 insertions, 0 deletions
diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h
new file mode 100644
index 0000000..b519d26
--- /dev/null
+++ b/gcc/analyzer/svalue.h
@@ -0,0 +1,1150 @@
+/* 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 */