| /* Symbolic values. |
| Copyright (C) 2019-2022 Free Software Foundation, Inc. |
| Contributed by David Malcolm <dmalcolm@redhat.com>. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #ifndef GCC_ANALYZER_SVALUE_H |
| #define GCC_ANALYZER_SVALUE_H |
| |
| #include "analyzer/complexity.h" |
| #include "analyzer/store.h" |
| #include "analyzer/program-point.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_REPEATED, |
| SK_BITS_WITHIN, |
| SK_UNMERGEABLE, |
| SK_PLACEHOLDER, |
| SK_WIDENING, |
| SK_COMPOUND, |
| SK_CONJURED, |
| SK_ASM_OUTPUT, |
| SK_CONST_FN_RESULT |
| }; |
| |
| /* 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 |
| repeated_svalue (SK_REPEATED): repeating an svalue to fill a larger region |
| bits_within_svalue (SK_BITS_WITHIN): a range of bits/bytes within a larger |
| svalue |
| 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 |
| asm_output_svalue (SK_ASM_OUTPUT): an output from a deterministic |
| asm stmt. |
| const_fn_result_svalue (SK_CONST_FN_RESULT): the return value from |
| a function with __attribute((const)) for given inputs. */ |
| |
| /* 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 repeated_svalue * |
| dyn_cast_repeated_svalue () const { return NULL; } |
| virtual const bits_within_svalue * |
| dyn_cast_bits_within_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; } |
| virtual const asm_output_svalue * |
| dyn_cast_asm_output_svalue () const { return NULL; } |
| virtual const const_fn_result_svalue * |
| dyn_cast_const_fn_result_svalue () const { return NULL; } |
| |
| tree maybe_get_constant () const; |
| const region *maybe_get_region () 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 *); |
| |
| bool involves_p (const svalue *other) const; |
| |
| const svalue * |
| extract_bit_range (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const; |
| |
| virtual const svalue * |
| maybe_fold_bits_within (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const; |
| |
| virtual bool all_zeroes_p () const; |
| |
| /* Can this svalue be involved in constraints and sm-state? |
| Most can, but UNKNOWN and POISONED svalues are singletons |
| per-type and thus it's meaningless for them to "have state". */ |
| virtual bool can_have_associated_state_p () const { return true; } |
| |
| const region *maybe_get_deref_base_region () const; |
| |
| 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 = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| 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; |
| bool implicitly_live_p (const svalue_set *, |
| const region_model *) 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 = false; |
| }; |
| |
| 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); |
| |
| const svalue * |
| maybe_fold_bits_within (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const final override; |
| |
| bool all_zeroes_p () const final override; |
| |
| 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; |
| |
| const svalue * |
| maybe_fold_bits_within (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const final override; |
| |
| /* Unknown values are singletons per-type, so can't have state. */ |
| bool can_have_associated_state_p () const final override { return false; } |
| }; |
| |
| /* An enum describing a particular kind of "poisoned" value. */ |
| |
| enum poison_kind |
| { |
| /* For use to describe uninitialized memory. */ |
| POISON_KIND_UNINIT, |
| |
| /* 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 = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| 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; |
| |
| const svalue * |
| maybe_fold_bits_within (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const final override; |
| |
| enum poison_kind get_poison_kind () const { return m_kind; } |
| |
| /* Poisoned svalues are singletons per-type, so can't have state. */ |
| bool can_have_associated_state_p () const final override { return false; } |
| |
| 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 = false; |
| }; |
| |
| 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 = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| 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 = false; |
| }; |
| |
| 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; |
| |
| bool initial_value_of_param_p () const; |
| |
| 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 = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| 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) |
| { |
| gcc_assert (arg->can_have_associated_state_p ()); |
| } |
| |
| 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; } |
| |
| const svalue * |
| maybe_fold_bits_within (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const final override; |
| |
| 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 = false; |
| }; |
| |
| 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 = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| 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) |
| { |
| gcc_assert (arg0->can_have_associated_state_p ()); |
| gcc_assert (arg1->can_have_associated_state_p ()); |
| } |
| |
| 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 = false; |
| }; |
| |
| 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 = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| 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 = false; |
| }; |
| |
| namespace ana { |
| |
| /* Concrete subclass of svalue representing repeating an inner svalue |
| (possibly not a whole number of times) to fill a larger region of |
| type TYPE of size OUTER_SIZE bytes. */ |
| |
| class repeated_svalue : public svalue |
| { |
| public: |
| /* A support class for uniquifying instances of repeated_svalue. */ |
| struct key_t |
| { |
| key_t (tree type, |
| const svalue *outer_size, |
| const svalue *inner_svalue) |
| : m_type (type), m_outer_size (outer_size), m_inner_svalue (inner_svalue) |
| {} |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_type); |
| hstate.add_ptr (m_outer_size); |
| hstate.add_ptr (m_inner_svalue); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_type == other.m_type |
| && m_outer_size == other.m_outer_size |
| && m_inner_svalue == other.m_inner_svalue); |
| } |
| |
| void mark_deleted () { m_type = reinterpret_cast<tree> (1); } |
| void mark_empty () { m_type = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| tree m_type; |
| const svalue *m_outer_size; |
| const svalue *m_inner_svalue; |
| }; |
| repeated_svalue (tree type, |
| const svalue *outer_size, |
| const svalue *inner_svalue); |
| |
| enum svalue_kind get_kind () const final override { return SK_REPEATED; } |
| const repeated_svalue *dyn_cast_repeated_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 svalue *get_outer_size () const { return m_outer_size; } |
| const svalue *get_inner_svalue () const { return m_inner_svalue; } |
| |
| bool all_zeroes_p () const final override; |
| |
| const svalue * |
| maybe_fold_bits_within (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const final override; |
| |
| private: |
| const svalue *m_outer_size; |
| const svalue *m_inner_svalue; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const repeated_svalue *>::test (const svalue *sval) |
| { |
| return sval->get_kind () == SK_REPEATED; |
| } |
| |
| template <> struct default_hash_traits<repeated_svalue::key_t> |
| : public member_function_hash_traits<repeated_svalue::key_t> |
| { |
| static const bool empty_zero_p = false; |
| }; |
| |
| namespace ana { |
| |
| /* A range of bits/bytes within another svalue |
| e.g. bytes 5-39 of INITIAL_SVALUE(R). |
| These can be generated for prefixes and suffixes when part of a binding |
| is clobbered, so that we don't lose too much information. */ |
| |
| class bits_within_svalue : public svalue |
| { |
| public: |
| /* A support class for uniquifying instances of bits_within_svalue. */ |
| struct key_t |
| { |
| key_t (tree type, |
| const bit_range &bits, |
| const svalue *inner_svalue) |
| : m_type (type), m_bits (bits), m_inner_svalue (inner_svalue) |
| {} |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_type); |
| hstate.add_ptr (m_inner_svalue); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| return (m_type == other.m_type |
| && m_bits == other.m_bits |
| && m_inner_svalue == other.m_inner_svalue); |
| } |
| |
| void mark_deleted () { m_type = reinterpret_cast<tree> (1); } |
| void mark_empty () { m_type = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| tree m_type; |
| bit_range m_bits; |
| const svalue *m_inner_svalue; |
| }; |
| bits_within_svalue (tree type, |
| const bit_range &bits, |
| const svalue *inner_svalue); |
| |
| enum svalue_kind get_kind () const final override { return SK_BITS_WITHIN; } |
| const bits_within_svalue * |
| dyn_cast_bits_within_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 bit_range &get_bits () const { return m_bits; } |
| const svalue *get_inner_svalue () const { return m_inner_svalue; } |
| |
| const svalue * |
| maybe_fold_bits_within (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const final override; |
| |
| private: |
| const bit_range m_bits; |
| const svalue *m_inner_svalue; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const bits_within_svalue *>::test (const svalue *sval) |
| { |
| return sval->get_kind () == SK_BITS_WITHIN; |
| } |
| |
| template <> struct default_hash_traits<bits_within_svalue::key_t> |
| : public member_function_hash_traits<bits_within_svalue::key_t> |
| { |
| static const bool empty_zero_p = false; |
| }; |
| |
| 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 <const placeholder_svalue *>::test (const 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 function_point &point, |
| const svalue *base_sval, const svalue *iter_sval) |
| : m_type (type), m_point (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 = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| 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 function_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), |
| m_base_sval (base_sval), m_iter_sval (iter_sval) |
| { |
| gcc_assert (base_sval->can_have_associated_state_p ()); |
| gcc_assert (iter_sval->can_have_associated_state_p ()); |
| } |
| |
| 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 <const widening_svalue *>::test (const 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 = false; |
| }; |
| |
| 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 = reinterpret_cast<tree> (2); } |
| bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } |
| bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } |
| |
| 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); |
| } |
| |
| const svalue * |
| maybe_fold_bits_within (tree type, |
| const bit_range &subrange, |
| region_model_manager *mgr) const final override; |
| |
| private: |
| static complexity calc_complexity (const binding_map &map); |
| |
| binding_map m_map; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const compound_svalue *>::test (const 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 = false; |
| }; |
| |
| namespace ana { |
| |
| /* A bundle of state for purging information from a program_state about |
| a conjured_svalue. We pass this whenever calling |
| get_or_create_conjured_svalue, so that if the program_state already |
| has information about this conjured_svalue on an execution path, we |
| can purge that information, to avoid the analyzer confusing the two |
| values as being the same. */ |
| |
| class conjured_purge |
| { |
| public: |
| conjured_purge (region_model *model, region_model_context *ctxt) |
| : m_model (model), m_ctxt (ctxt) |
| { |
| } |
| void purge (const conjured_svalue *sval) const; |
| |
| private: |
| region_model *m_model; |
| region_model_context *m_ctxt; |
| }; |
| |
| /* 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: |
| /* 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 <const conjured_svalue *>::test (const 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 output from a deterministic asm stmt, where we want to identify a |
| particular unknown value, rather than resorting to the unknown_value |
| singleton. |
| |
| Comparisons of variables that share the same asm_output_svalue are known |
| to be equal, even if we don't know what the value is. */ |
| |
| class asm_output_svalue : public svalue |
| { |
| public: |
| /* Imposing an upper limit and using a (small) array allows key_t |
| to avoid memory management. */ |
| static const unsigned MAX_INPUTS = 2; |
| |
| /* A support class for uniquifying instances of asm_output_svalue. */ |
| struct key_t |
| { |
| key_t (tree type, |
| const char *asm_string, |
| unsigned output_idx, |
| const vec<const svalue *> &inputs) |
| : m_type (type), m_asm_string (asm_string), m_output_idx (output_idx), |
| m_num_inputs (inputs.length ()) |
| { |
| gcc_assert (inputs.length () <= MAX_INPUTS); |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| m_input_arr[i] = inputs[i]; |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_type); |
| /* We don't bother hashing m_asm_str. */ |
| hstate.add_int (m_output_idx); |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| hstate.add_ptr (m_input_arr[i]); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| if (!(m_type == other.m_type |
| && 0 == (strcmp (m_asm_string, other.m_asm_string)) |
| && m_output_idx == other.m_output_idx |
| && m_num_inputs == other.m_num_inputs)) |
| return false; |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| if (m_input_arr[i] != other.m_input_arr[i]) |
| return false; |
| return true; |
| } |
| |
| /* Use m_asm_string to mark empty/deleted, as m_type can be NULL for |
| legitimate instances. */ |
| void mark_deleted () { m_asm_string = reinterpret_cast<const char *> (1); } |
| void mark_empty () { m_asm_string = NULL; } |
| bool is_deleted () const |
| { |
| return m_asm_string == reinterpret_cast<const char *> (1); |
| } |
| bool is_empty () const { return m_asm_string == NULL; } |
| |
| tree m_type; |
| const char *m_asm_string; |
| unsigned m_output_idx; |
| unsigned m_num_inputs; |
| const svalue *m_input_arr[MAX_INPUTS]; |
| }; |
| |
| asm_output_svalue (tree type, |
| const char *asm_string, |
| unsigned output_idx, |
| unsigned num_outputs, |
| const vec<const svalue *> &inputs) |
| : svalue (complexity::from_vec_svalue (inputs), type), |
| m_asm_string (asm_string), |
| m_output_idx (output_idx), |
| m_num_outputs (num_outputs), |
| m_num_inputs (inputs.length ()) |
| { |
| gcc_assert (inputs.length () <= MAX_INPUTS); |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| m_input_arr[i] = inputs[i]; |
| } |
| |
| enum svalue_kind get_kind () const final override { return SK_ASM_OUTPUT; } |
| const asm_output_svalue * |
| dyn_cast_asm_output_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 char *get_asm_string () const { return m_asm_string; } |
| unsigned get_output_idx () const { return m_output_idx; } |
| unsigned get_num_outputs () const { return m_num_outputs; } |
| unsigned get_num_inputs () const { return m_num_inputs; } |
| const svalue *get_input (unsigned idx) const { return m_input_arr[idx]; } |
| |
| private: |
| void dump_input (pretty_printer *pp, |
| unsigned input_idx, |
| const svalue *sval, |
| bool simple) const; |
| unsigned input_idx_to_asm_idx (unsigned input_idx) const; |
| |
| const char *m_asm_string; |
| unsigned m_output_idx; |
| |
| /* We capture this so that we can offset the input indices |
| to match the %0, %1, %2 in the asm_string when dumping. */ |
| unsigned m_num_outputs; |
| |
| unsigned m_num_inputs; |
| const svalue *m_input_arr[MAX_INPUTS]; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const asm_output_svalue *>::test (const svalue *sval) |
| { |
| return sval->get_kind () == SK_ASM_OUTPUT; |
| } |
| |
| template <> struct default_hash_traits<asm_output_svalue::key_t> |
| : public member_function_hash_traits<asm_output_svalue::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| namespace ana { |
| |
| /* The return value from a function with __attribute((const)) for given |
| inputs, provided that we don't have too many inputs, and all of them |
| are deterministic. |
| |
| Comparisons of variables that share the same const_fn_result_svalue are known |
| to be equal, even if we don't know what the value is. */ |
| |
| class const_fn_result_svalue : public svalue |
| { |
| public: |
| /* Imposing an upper limit and using a (small) array allows key_t |
| to avoid memory management. */ |
| static const unsigned MAX_INPUTS = 2; |
| |
| /* A support class for uniquifying instances of const_fn_result_svalue. */ |
| struct key_t |
| { |
| key_t (tree type, |
| tree fndecl, |
| const vec<const svalue *> &inputs) |
| : m_type (type), m_fndecl (fndecl), |
| m_num_inputs (inputs.length ()) |
| { |
| gcc_assert (inputs.length () <= MAX_INPUTS); |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| m_input_arr[i] = inputs[i]; |
| } |
| |
| hashval_t hash () const |
| { |
| inchash::hash hstate; |
| hstate.add_ptr (m_type); |
| hstate.add_ptr (m_fndecl); |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| hstate.add_ptr (m_input_arr[i]); |
| return hstate.end (); |
| } |
| |
| bool operator== (const key_t &other) const |
| { |
| if (!(m_type == other.m_type |
| && m_fndecl == other.m_fndecl |
| && m_num_inputs == other.m_num_inputs)) |
| return false; |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| if (m_input_arr[i] != other.m_input_arr[i]) |
| return false; |
| return true; |
| } |
| |
| /* Use m_fndecl to mark empty/deleted. */ |
| void mark_deleted () { m_fndecl = reinterpret_cast<tree> (1); } |
| void mark_empty () { m_fndecl = NULL; } |
| bool is_deleted () const |
| { |
| return m_fndecl == reinterpret_cast<tree> (1); |
| } |
| bool is_empty () const { return m_fndecl == NULL; } |
| |
| tree m_type; |
| tree m_fndecl; |
| unsigned m_num_inputs; |
| const svalue *m_input_arr[MAX_INPUTS]; |
| }; |
| |
| const_fn_result_svalue (tree type, |
| tree fndecl, |
| const vec<const svalue *> &inputs) |
| : svalue (complexity::from_vec_svalue (inputs), type), |
| m_fndecl (fndecl), |
| m_num_inputs (inputs.length ()) |
| { |
| gcc_assert (inputs.length () <= MAX_INPUTS); |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| m_input_arr[i] = inputs[i]; |
| } |
| |
| enum svalue_kind get_kind () const final override |
| { |
| return SK_CONST_FN_RESULT; |
| } |
| const const_fn_result_svalue * |
| dyn_cast_const_fn_result_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; |
| |
| tree get_fndecl () const { return m_fndecl; } |
| unsigned get_num_inputs () const { return m_num_inputs; } |
| const svalue *get_input (unsigned idx) const { return m_input_arr[idx]; } |
| |
| private: |
| void dump_input (pretty_printer *pp, |
| unsigned input_idx, |
| const svalue *sval, |
| bool simple) const; |
| |
| tree m_fndecl; |
| unsigned m_num_inputs; |
| const svalue *m_input_arr[MAX_INPUTS]; |
| }; |
| |
| } // namespace ana |
| |
| template <> |
| template <> |
| inline bool |
| is_a_helper <const const_fn_result_svalue *>::test (const svalue *sval) |
| { |
| return sval->get_kind () == SK_CONST_FN_RESULT; |
| } |
| |
| template <> struct default_hash_traits<const_fn_result_svalue::key_t> |
| : public member_function_hash_traits<const_fn_result_svalue::key_t> |
| { |
| static const bool empty_zero_p = true; |
| }; |
| |
| #endif /* GCC_ANALYZER_SVALUE_H */ |