aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/svalue.h
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/analyzer/svalue.h')
-rw-r--r--gcc/analyzer/svalue.h448
1 files changed, 416 insertions, 32 deletions
diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h
index 672a89c..63f7d15 100644
--- a/gcc/analyzer/svalue.h
+++ b/gcc/analyzer/svalue.h
@@ -41,11 +41,14 @@ enum svalue_kind
SK_UNARYOP,
SK_BINOP,
SK_SUB,
+ SK_REPEATED,
+ SK_BITS_WITHIN,
SK_UNMERGEABLE,
SK_PLACEHOLDER,
SK_WIDENING,
SK_COMPOUND,
- SK_CONJURED
+ SK_CONJURED,
+ SK_ASM_OUTPUT
};
/* svalue and its subclasses.
@@ -63,13 +66,18 @@ enum svalue_kind
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. */
+ conjured_svalue (SK_CONJURED): a value arising from a stmt
+ asm_output_svalue (SK_ASM_OUTPUT): an output from a deterministic
+ asm stmt. */
/* An abstract base class representing a value held by a region of memory. */
@@ -107,6 +115,10 @@ public:
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 *
@@ -115,8 +127,11 @@ public:
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; }
tree maybe_get_constant () const;
+ const region *maybe_get_region () const;
const svalue *maybe_undo_cast () const;
const svalue *unwrap_any_unmergeable () const;
@@ -136,6 +151,25 @@ public:
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; }
+
protected:
svalue (complexity c, tree type)
: m_complexity (c), m_type (type)
@@ -173,9 +207,9 @@ public:
}
void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
- void mark_empty () { m_type = NULL_TREE; }
+ 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 == NULL_TREE; }
+ bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }
tree m_type;
const region *m_reg;
@@ -220,7 +254,7 @@ is_a_helper <const region_svalue *>::test (const svalue *sval)
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;
+ static const bool empty_zero_p = false;
};
namespace ana {
@@ -251,6 +285,13 @@ public:
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;
};
@@ -283,12 +324,23 @@ public:
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,
@@ -325,9 +377,9 @@ public:
}
void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
- void mark_empty () { m_type = NULL_TREE; }
+ 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 == NULL_TREE; }
+ bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }
enum poison_kind m_kind;
tree m_type;
@@ -343,8 +395,16 @@ public:
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;
};
@@ -362,7 +422,7 @@ is_a_helper <const poisoned_svalue *>::test (const svalue *sval)
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;
+ static const bool empty_zero_p = false;
};
namespace ana {
@@ -424,9 +484,9 @@ public:
}
void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
- void mark_empty () { m_type = NULL_TREE; }
+ 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 == NULL_TREE; }
+ bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }
setjmp_record m_record;
tree m_type;
@@ -465,7 +525,7 @@ is_a_helper <const setjmp_svalue *>::test (const svalue *sval)
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;
+ static const bool empty_zero_p = false;
};
namespace ana {
@@ -546,9 +606,9 @@ public:
}
void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
- void mark_empty () { m_type = NULL_TREE; }
+ 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 == NULL_TREE; }
+ bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }
tree m_type;
enum tree_code m_op;
@@ -558,6 +618,7 @@ public:
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; }
@@ -572,6 +633,11 @@ public:
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;
@@ -590,7 +656,7 @@ is_a_helper <const unaryop_svalue *>::test (const svalue *sval)
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;
+ static const bool empty_zero_p = false;
};
namespace ana {
@@ -628,9 +694,9 @@ public:
}
void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
- void mark_empty () { m_type = NULL_TREE; }
+ 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 == NULL_TREE; }
+ bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }
tree m_type;
enum tree_code m_op;
@@ -645,6 +711,8 @@ public:
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; }
@@ -681,7 +749,7 @@ is_a_helper <const binop_svalue *>::test (const svalue *sval)
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;
+ static const bool empty_zero_p = false;
};
namespace ana {
@@ -717,9 +785,9 @@ public:
}
void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
- void mark_empty () { m_type = NULL_TREE; }
+ 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 == NULL_TREE; }
+ bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }
tree m_type;
const svalue *m_parent_svalue;
@@ -760,7 +828,182 @@ is_a_helper <const sub_svalue *>::test (const svalue *sval)
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;
+ 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 {
@@ -840,7 +1083,7 @@ public:
template <>
template <>
inline bool
-is_a_helper <placeholder_svalue *>::test (svalue *sval)
+is_a_helper <const placeholder_svalue *>::test (const svalue *sval)
{
return sval->get_kind () == SK_PLACEHOLDER;
}
@@ -886,9 +1129,9 @@ public:
}
void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
- void mark_empty () { m_type = NULL_TREE; }
+ 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 == NULL_TREE; }
+ bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }
tree m_type;
function_point m_point;
@@ -911,6 +1154,8 @@ public:
m_point (point.get_function_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; }
@@ -942,7 +1187,7 @@ public:
template <>
template <>
inline bool
-is_a_helper <widening_svalue *>::test (svalue *sval)
+is_a_helper <const widening_svalue *>::test (const svalue *sval)
{
return sval->get_kind () == SK_WIDENING;
}
@@ -950,7 +1195,7 @@ is_a_helper <widening_svalue *>::test (svalue *sval)
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;
+ static const bool empty_zero_p = false;
};
namespace ana {
@@ -998,9 +1243,9 @@ public:
}
void mark_deleted () { m_type = reinterpret_cast<tree> (1); }
- void mark_empty () { m_type = NULL_TREE; }
+ 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 == NULL_TREE; }
+ bool is_empty () const { return m_type == reinterpret_cast<tree> (2); }
tree m_type;
const binding_map *m_map_ptr;
@@ -1027,6 +1272,11 @@ public:
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);
@@ -1038,7 +1288,7 @@ public:
template <>
template <>
inline bool
-is_a_helper <compound_svalue *>::test (svalue *sval)
+is_a_helper <const compound_svalue *>::test (const svalue *sval)
{
return sval->get_kind () == SK_COMPOUND;
}
@@ -1046,7 +1296,7 @@ is_a_helper <compound_svalue *>::test (svalue *sval)
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;
+ static const bool empty_zero_p = false;
};
namespace ana {
@@ -1071,8 +1321,6 @@ namespace ana {
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
{
@@ -1140,7 +1388,7 @@ public:
template <>
template <>
inline bool
-is_a_helper <conjured_svalue *>::test (svalue *sval)
+is_a_helper <const conjured_svalue *>::test (const svalue *sval)
{
return sval->get_kind () == SK_CONJURED;
}
@@ -1151,4 +1399,140 @@ template <> struct default_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_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;
+};
#endif /* GCC_ANALYZER_SVALUE_H */