/* Symbolic values. Copyright (C) 2019-2022 Free Software Foundation, Inc. Contributed by David Malcolm . 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 . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tree.h" #include "diagnostic-core.h" #include "gimple-pretty-print.h" #include "function.h" #include "basic-block.h" #include "gimple.h" #include "gimple-iterator.h" #include "diagnostic-core.h" #include "graphviz.h" #include "options.h" #include "cgraph.h" #include "tree-dfa.h" #include "stringpool.h" #include "convert.h" #include "target.h" #include "fold-const.h" #include "tree-pretty-print.h" #include "tristate.h" #include "bitmap.h" #include "selftest.h" #include "function.h" #include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "options.h" #include "cgraph.h" #include "cfg.h" #include "digraph.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/svalue.h" #include "analyzer/region-model.h" #if ENABLE_ANALYZER namespace ana { static int cmp_csts_and_types (const_tree cst1, const_tree cst2); /* class svalue and its various subclasses. */ /* class svalue. */ /* Dump a representation of this svalue to stderr. */ DEBUG_FUNCTION void svalue::dump (bool simple) const { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_show_color (&pp) = pp_show_color (global_dc->printer); pp.buffer->stream = stderr; dump_to_pp (&pp, simple); pp_newline (&pp); pp_flush (&pp); } /* Generate a textual representation of this svalue for debugging purposes. */ label_text svalue::get_desc (bool simple) const { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; dump_to_pp (&pp, simple); return label_text::take (xstrdup (pp_formatted_text (&pp))); } /* Return a new json::string describing the svalue. */ json::value * svalue::to_json () const { label_text desc = get_desc (true); json::value *sval_js = new json::string (desc.get ()); return sval_js; } /* If this svalue is a constant_svalue, return the underlying tree constant. Otherwise return NULL_TREE. */ tree svalue::maybe_get_constant () const { const svalue *sval = unwrap_any_unmergeable (); if (const constant_svalue *cst_sval = sval->dyn_cast_constant_svalue ()) return cst_sval->get_constant (); else return NULL_TREE; } /* If this svalue is a region_svalue, return the region it points to. Otherwise return NULL. */ const region * svalue::maybe_get_region () const { if (const region_svalue *region_sval = dyn_cast_region_svalue ()) return region_sval->get_pointee (); else return NULL; } /* If this svalue is a cast (i.e a unaryop NOP_EXPR or VIEW_CONVERT_EXPR), return the underlying svalue. Otherwise return NULL. */ const svalue * svalue::maybe_undo_cast () const { if (const unaryop_svalue *unaryop_sval = dyn_cast_unaryop_svalue ()) { enum tree_code op = unaryop_sval->get_op (); if (op == NOP_EXPR || op == VIEW_CONVERT_EXPR) return unaryop_sval->get_arg (); } return NULL; } /* If this svalue is an unmergeable decorator around another svalue, return the underlying svalue. Otherwise return this svalue. */ const svalue * svalue::unwrap_any_unmergeable () const { if (const unmergeable_svalue *unmergeable = dyn_cast_unmergeable_svalue ()) return unmergeable->get_arg (); return this; } /* Attempt to merge THIS with OTHER, returning the merged svalue. Return NULL if not mergeable. */ const svalue * svalue::can_merge_p (const svalue *other, region_model_manager *mgr, model_merger *merger) const { if (!(get_type () && other->get_type ())) return NULL; if (!types_compatible_p (get_type (), other->get_type ())) return NULL; /* Reject attempts to merge unmergeable svalues. */ if ((get_kind () == SK_UNMERGEABLE) || (other->get_kind () == SK_UNMERGEABLE)) return NULL; /* Reject attempts to merge poisoned svalues with other svalues (either non-poisoned, or other kinds of poison), so that e.g. we identify paths in which a variable is conditionally uninitialized. */ if (get_kind () == SK_POISONED || other->get_kind () == SK_POISONED) return NULL; /* Reject attempts to merge NULL pointers with not-NULL-pointers. */ if (POINTER_TYPE_P (get_type ())) { bool null0 = false; bool null1 = false; if (tree cst0 = maybe_get_constant ()) if (zerop (cst0)) null0 = true; if (tree cst1 = other->maybe_get_constant ()) if (zerop (cst1)) null1 = true; if (null0 != null1) return NULL; } /* Reject merging svalues that have non-purgable sm-state, to avoid falsely reporting memory leaks by merging them with something else. */ if (!merger->mergeable_svalue_p (this)) return NULL; if (!merger->mergeable_svalue_p (other)) return NULL; /* Widening. */ /* Merge: (new_cst, existing_cst) -> widen (existing, new). */ if (maybe_get_constant () && other->maybe_get_constant ()) { return mgr->get_or_create_widening_svalue (other->get_type (), merger->m_point, other, this); } /* Merger of: this: BINOP (X, OP, CST) other: X, where X is non-widening to: WIDENING (other, this). */ if (const binop_svalue *binop_sval = dyn_cast_binop_svalue ()) if (binop_sval->get_arg0 () == other && binop_sval->get_arg1 ()->get_kind () == SK_CONSTANT && other->get_kind () != SK_WIDENING) return mgr->get_or_create_widening_svalue (other->get_type (), merger->m_point, other, this); /* Merge: (Widen(existing_val, V), existing_val) -> Widen (existing_val, V) and thus get a fixed point. */ if (const widening_svalue *widen_sval = dyn_cast_widening_svalue ()) { if (other == widen_sval->get_base_svalue ()) return this; if (other == widen_sval->get_iter_svalue ()) return this; } if (const binop_svalue *binop_sval = dyn_cast_binop_svalue ()) if (const widening_svalue *widen_arg0 = binop_sval->get_arg0 ()->dyn_cast_widening_svalue ()) { if (other == binop_sval->get_arg1 ()) { /* Merger of: (Widen(..., OTHER) BINOP X) and : OTHER to : (Widen(..., OTHER) BINOP X) e.g. merge of Widen(0, 1) + 1 with 1 to the Widen(0, 1) + 1. */ return this; } /* Merger of : (Widen() BINOP X) and : Widen() to : Widen() e.g. merge of Widen(0, 1) + 1 and Widen(0, 1) to Widen(0, 1). However, we want to update constraints for this case, since we're considering another iteration. Presumably we also want to ensure that it converges; we don't want a descending chain of constraints. */ if (other == widen_arg0) { return widen_arg0; } /* Merger of: this: BINOP(WIDENING(BASE, BINOP(BASE, X)), X) other: BINOP(BASE, X) to: WIDENING(BASE, BINOP(BASE, X)). */ if (widen_arg0->get_iter_svalue () == other) if (const binop_svalue *other_binop_sval = other->dyn_cast_binop_svalue ()) if (other_binop_sval->get_arg0 () == widen_arg0->get_base_svalue () && other_binop_sval->get_arg1 () == binop_sval->get_arg1 ()) return widen_arg0; } return mgr->get_or_create_unknown_svalue (get_type ()); } /* Determine if this svalue is either within LIVE_SVALUES, or is implicitly live with respect to LIVE_SVALUES and MODEL. LIVE_SVALUES can be NULL, in which case determine if this svalue is intrinsically live. */ bool svalue::live_p (const svalue_set *live_svalues, const region_model *model) const { /* Determine if SVAL is explicitly live. */ if (live_svalues) if (const_cast (live_svalues)->contains (this)) return true; /* Otherwise, determine if SVAL is implicitly live due to being made of other live svalues. */ return implicitly_live_p (live_svalues, model); } /* Base implementation of svalue::implicitly_live_p. */ bool svalue::implicitly_live_p (const svalue_set *, const region_model *) const { return false; } /* Comparator for imposing a deterministic order on constants that are of the same type. */ static int cmp_csts_same_type (const_tree cst1, const_tree cst2) { gcc_assert (TREE_TYPE (cst1) == TREE_TYPE (cst2)); gcc_assert (TREE_CODE (cst1) == TREE_CODE (cst2)); switch (TREE_CODE (cst1)) { default: gcc_unreachable (); case INTEGER_CST: return tree_int_cst_compare (cst1, cst2); case STRING_CST: return strcmp (TREE_STRING_POINTER (cst1), TREE_STRING_POINTER (cst2)); case REAL_CST: /* Impose an arbitrary but deterministic order. */ return memcmp (TREE_REAL_CST_PTR (cst1), TREE_REAL_CST_PTR (cst2), sizeof (real_value)); case COMPLEX_CST: if (int cmp_real = cmp_csts_and_types (TREE_REALPART (cst1), TREE_REALPART (cst2))) return cmp_real; return cmp_csts_and_types (TREE_IMAGPART (cst1), TREE_IMAGPART (cst2)); case VECTOR_CST: if (int cmp_log2_npatterns = ((int)VECTOR_CST_LOG2_NPATTERNS (cst1) - (int)VECTOR_CST_LOG2_NPATTERNS (cst2))) return cmp_log2_npatterns; if (int cmp_nelts_per_pattern = ((int)VECTOR_CST_NELTS_PER_PATTERN (cst1) - (int)VECTOR_CST_NELTS_PER_PATTERN (cst2))) return cmp_nelts_per_pattern; unsigned encoded_nelts = vector_cst_encoded_nelts (cst1); for (unsigned i = 0; i < encoded_nelts; i++) { const_tree elt1 = VECTOR_CST_ENCODED_ELT (cst1, i); const_tree elt2 = VECTOR_CST_ENCODED_ELT (cst2, i); if (int el_cmp = cmp_csts_and_types (elt1, elt2)) return el_cmp; } return 0; } } /* Comparator for imposing a deterministic order on constants that might not be of the same type. */ static int cmp_csts_and_types (const_tree cst1, const_tree cst2) { int t1 = TYPE_UID (TREE_TYPE (cst1)); int t2 = TYPE_UID (TREE_TYPE (cst2)); if (int cmp_type = t1 - t2) return cmp_type; return cmp_csts_same_type (cst1, cst2); } /* Comparator for imposing a deterministic order on svalues. */ int svalue::cmp_ptr (const svalue *sval1, const svalue *sval2) { if (sval1 == sval2) return 0; if (int cmp_kind = sval1->get_kind () - sval2->get_kind ()) return cmp_kind; int t1 = sval1->get_type () ? TYPE_UID (sval1->get_type ()) : -1; int t2 = sval2->get_type () ? TYPE_UID (sval2->get_type ()) : -1; if (int cmp_type = t1 - t2) return cmp_type; switch (sval1->get_kind ()) { default: gcc_unreachable (); case SK_REGION: { const region_svalue *region_sval1 = (const region_svalue *)sval1; const region_svalue *region_sval2 = (const region_svalue *)sval2; return region::cmp_ids (region_sval1->get_pointee (), region_sval2->get_pointee ()); } break; case SK_CONSTANT: { const constant_svalue *constant_sval1 = (const constant_svalue *)sval1; const constant_svalue *constant_sval2 = (const constant_svalue *)sval2; const_tree cst1 = constant_sval1->get_constant (); const_tree cst2 = constant_sval2->get_constant (); return cmp_csts_same_type (cst1, cst2); } break; case SK_UNKNOWN: { gcc_assert (sval1 == sval2); return 0; } break; case SK_POISONED: { const poisoned_svalue *poisoned_sval1 = (const poisoned_svalue *)sval1; const poisoned_svalue *poisoned_sval2 = (const poisoned_svalue *)sval2; return (poisoned_sval1->get_poison_kind () - poisoned_sval2->get_poison_kind ()); } break; case SK_SETJMP: { const setjmp_svalue *setjmp_sval1 = (const setjmp_svalue *)sval1; const setjmp_svalue *setjmp_sval2 = (const setjmp_svalue *)sval2; const setjmp_record &rec1 = setjmp_sval1->get_setjmp_record (); const setjmp_record &rec2 = setjmp_sval2->get_setjmp_record (); return setjmp_record::cmp (rec1, rec2); } break; case SK_INITIAL: { const initial_svalue *initial_sval1 = (const initial_svalue *)sval1; const initial_svalue *initial_sval2 = (const initial_svalue *)sval2; return region::cmp_ids (initial_sval1->get_region (), initial_sval2->get_region ()); } break; case SK_UNARYOP: { const unaryop_svalue *unaryop_sval1 = (const unaryop_svalue *)sval1; const unaryop_svalue *unaryop_sval2 = (const unaryop_svalue *)sval2; if (int op_cmp = unaryop_sval1->get_op () - unaryop_sval2->get_op ()) return op_cmp; return svalue::cmp_ptr (unaryop_sval1->get_arg (), unaryop_sval2->get_arg ()); } break; case SK_BINOP: { const binop_svalue *binop_sval1 = (const binop_svalue *)sval1; const binop_svalue *binop_sval2 = (const binop_svalue *)sval2; if (int op_cmp = binop_sval1->get_op () - binop_sval2->get_op ()) return op_cmp; if (int arg0_cmp = svalue::cmp_ptr (binop_sval1->get_arg0 (), binop_sval2->get_arg0 ())) return arg0_cmp; return svalue::cmp_ptr (binop_sval1->get_arg1 (), binop_sval2->get_arg1 ()); } break; case SK_SUB: { const sub_svalue *sub_sval1 = (const sub_svalue *)sval1; const sub_svalue *sub_sval2 = (const sub_svalue *)sval2; if (int parent_cmp = svalue::cmp_ptr (sub_sval1->get_parent (), sub_sval2->get_parent ())) return parent_cmp; return region::cmp_ids (sub_sval1->get_subregion (), sub_sval2->get_subregion ()); } break; case SK_REPEATED: { const repeated_svalue *repeated_sval1 = (const repeated_svalue *)sval1; const repeated_svalue *repeated_sval2 = (const repeated_svalue *)sval2; return svalue::cmp_ptr (repeated_sval1->get_inner_svalue (), repeated_sval2->get_inner_svalue ()); } break; case SK_BITS_WITHIN: { const bits_within_svalue *bits_within_sval1 = (const bits_within_svalue *)sval1; const bits_within_svalue *bits_within_sval2 = (const bits_within_svalue *)sval2; if (int cmp = bit_range::cmp (bits_within_sval1->get_bits (), bits_within_sval2->get_bits ())) return cmp; return svalue::cmp_ptr (bits_within_sval1->get_inner_svalue (), bits_within_sval2->get_inner_svalue ()); } break; case SK_UNMERGEABLE: { const unmergeable_svalue *unmergeable_sval1 = (const unmergeable_svalue *)sval1; const unmergeable_svalue *unmergeable_sval2 = (const unmergeable_svalue *)sval2; return svalue::cmp_ptr (unmergeable_sval1->get_arg (), unmergeable_sval2->get_arg ()); } break; case SK_PLACEHOLDER: { const placeholder_svalue *placeholder_sval1 = (const placeholder_svalue *)sval1; const placeholder_svalue *placeholder_sval2 = (const placeholder_svalue *)sval2; return strcmp (placeholder_sval1->get_name (), placeholder_sval2->get_name ()); } break; case SK_WIDENING: { const widening_svalue *widening_sval1 = (const widening_svalue *)sval1; const widening_svalue *widening_sval2 = (const widening_svalue *)sval2; if (int point_cmp = function_point::cmp (widening_sval1->get_point (), widening_sval2->get_point ())) return point_cmp; if (int base_cmp = svalue::cmp_ptr (widening_sval1->get_base_svalue (), widening_sval2->get_base_svalue ())) return base_cmp; return svalue::cmp_ptr (widening_sval1->get_iter_svalue (), widening_sval2->get_iter_svalue ()); } break; case SK_COMPOUND: { const compound_svalue *compound_sval1 = (const compound_svalue *)sval1; const compound_svalue *compound_sval2 = (const compound_svalue *)sval2; return binding_map::cmp (compound_sval1->get_map (), compound_sval2->get_map ()); } break; case SK_CONJURED: { const conjured_svalue *conjured_sval1 = (const conjured_svalue *)sval1; const conjured_svalue *conjured_sval2 = (const conjured_svalue *)sval2; if (int stmt_cmp = (conjured_sval1->get_stmt ()->uid - conjured_sval2->get_stmt ()->uid)) return stmt_cmp; return region::cmp_ids (conjured_sval1->get_id_region (), conjured_sval2->get_id_region ()); } break; case SK_ASM_OUTPUT: { const asm_output_svalue *asm_output_sval1 = (const asm_output_svalue *)sval1; const asm_output_svalue *asm_output_sval2 = (const asm_output_svalue *)sval2; if (int asm_string_cmp = strcmp (asm_output_sval1->get_asm_string (), asm_output_sval2->get_asm_string ())) return asm_string_cmp; if (int output_idx_cmp = ((int)asm_output_sval1->get_output_idx () - (int)asm_output_sval2->get_output_idx ())) return output_idx_cmp; if (int cmp = ((int)asm_output_sval1->get_num_inputs () - (int)asm_output_sval2->get_num_inputs ())) return cmp; for (unsigned i = 0; i < asm_output_sval1->get_num_inputs (); i++) if (int input_cmp = svalue::cmp_ptr (asm_output_sval1->get_input (i), asm_output_sval2->get_input (i))) return input_cmp; return 0; } break; case SK_CONST_FN_RESULT: { const const_fn_result_svalue *const_fn_result_sval1 = (const const_fn_result_svalue *)sval1; const const_fn_result_svalue *const_fn_result_sval2 = (const const_fn_result_svalue *)sval2; int d1 = DECL_UID (const_fn_result_sval1->get_fndecl ()); int d2 = DECL_UID (const_fn_result_sval2->get_fndecl ()); if (int cmp_fndecl = d1 - d2) return cmp_fndecl; if (int cmp = ((int)const_fn_result_sval1->get_num_inputs () - (int)const_fn_result_sval2->get_num_inputs ())) return cmp; for (unsigned i = 0; i < const_fn_result_sval1->get_num_inputs (); i++) if (int input_cmp = svalue::cmp_ptr (const_fn_result_sval1->get_input (i), const_fn_result_sval2->get_input (i))) return input_cmp; return 0; } } } /* Comparator for use by vec::qsort. */ int svalue::cmp_ptr_ptr (const void *p1, const void *p2) { const svalue *sval1 = *(const svalue * const *)p1; const svalue *sval2 = *(const svalue * const *)p2; return cmp_ptr (sval1, sval2); } /* Subclass of visitor for use in implementing svalue::involves_p. */ class involvement_visitor : public visitor { public: involvement_visitor (const svalue *needle) : m_needle (needle), m_found (false) {} void visit_initial_svalue (const initial_svalue *candidate) final override { if (candidate == m_needle) m_found = true; } void visit_conjured_svalue (const conjured_svalue *candidate) final override { if (candidate == m_needle) m_found = true; } bool found_p () const { return m_found; } private: const svalue *m_needle; bool m_found; }; /* Return true iff this svalue is defined in terms of OTHER. */ bool svalue::involves_p (const svalue *other) const { /* Currently only implemented for these kinds. */ gcc_assert (other->get_kind () == SK_INITIAL || other->get_kind () == SK_CONJURED); involvement_visitor v (other); accept (&v); return v.found_p (); } /* Extract SUBRANGE from this value, of type TYPE. */ const svalue * svalue::extract_bit_range (tree type, const bit_range &subrange, region_model_manager *mgr) const { return mgr->get_or_create_bits_within (type, subrange, this); } /* Base implementation of svalue::maybe_fold_bits_within vfunc. */ const svalue * svalue::maybe_fold_bits_within (tree, const bit_range &, region_model_manager *) const { /* By default, don't fold. */ return NULL; } /* Base implementation of svalue::all_zeroes_p. Return true if this value is known to be all zeroes. */ bool svalue::all_zeroes_p () const { return false; } /* If this svalue is a pointer, attempt to determine the base region it points to. Return NULL on any problems. */ const region * svalue::maybe_get_deref_base_region () const { const svalue *iter = this; while (1) { switch (iter->get_kind ()) { default: return NULL; case SK_REGION: { const region_svalue *region_sval = as_a (iter); return region_sval->get_pointee ()->get_base_region (); } case SK_BINOP: { const binop_svalue *binop_sval = as_a (iter); switch (binop_sval->get_op ()) { case POINTER_PLUS_EXPR: /* If we have a symbolic value expressing pointer arithmetic, use the LHS. */ iter = binop_sval->get_arg0 (); continue; default: return NULL; } return NULL; } } } } /* class region_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for region_svalue. */ void region_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "&"); m_reg->dump_to_pp (pp, simple); } else { pp_string (pp, "region_svalue("); print_quoted_type (pp, get_type ()); pp_string (pp, ", "); m_reg->dump_to_pp (pp, simple); pp_string (pp, ")"); } } /* Implementation of svalue::accept vfunc for region_svalue. */ void region_svalue::accept (visitor *v) const { m_reg->accept (v); v->visit_region_svalue (this); } /* Implementation of svalue::implicitly_live_p vfunc for region_svalue. */ bool region_svalue::implicitly_live_p (const svalue_set *, const region_model *model) const { /* Pointers into clusters that have escaped should be treated as live. */ const region *base_reg = get_pointee ()->get_base_region (); const store *store = model->get_store (); if (const binding_cluster *c = store->get_cluster (base_reg)) if (c->escaped_p ()) return true; return false; } /* Evaluate the condition LHS OP RHS. Subroutine of region_model::eval_condition for when we have a pair of pointers. */ tristate region_svalue::eval_condition (const region_svalue *lhs, enum tree_code op, const region_svalue *rhs) { /* See if they point to the same region. */ const region *lhs_reg = lhs->get_pointee (); const region *rhs_reg = rhs->get_pointee (); bool ptr_equality = lhs_reg == rhs_reg; switch (op) { default: gcc_unreachable (); case EQ_EXPR: if (ptr_equality) return tristate::TS_TRUE; else return tristate::TS_FALSE; break; case NE_EXPR: if (ptr_equality) return tristate::TS_FALSE; else return tristate::TS_TRUE; break; case GE_EXPR: case LE_EXPR: if (ptr_equality) return tristate::TS_TRUE; break; case GT_EXPR: case LT_EXPR: if (ptr_equality) return tristate::TS_FALSE; break; } return tristate::TS_UNKNOWN; } /* class constant_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for constant_svalue. */ void constant_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "("); dump_tree (pp, get_type ()); pp_string (pp, ")"); dump_tree (pp, m_cst_expr); } else { pp_string (pp, "constant_svalue("); print_quoted_type (pp, get_type ()); pp_string (pp, ", "); dump_tree (pp, m_cst_expr); pp_string (pp, ")"); } } /* Implementation of svalue::accept vfunc for constant_svalue. */ void constant_svalue::accept (visitor *v) const { v->visit_constant_svalue (this); } /* Implementation of svalue::implicitly_live_p vfunc for constant_svalue. Constants are implicitly live. */ bool constant_svalue::implicitly_live_p (const svalue_set *, const region_model *) const { return true; } /* Evaluate the condition LHS OP RHS. Subroutine of region_model::eval_condition for when we have a pair of constants. */ tristate constant_svalue::eval_condition (const constant_svalue *lhs, enum tree_code op, const constant_svalue *rhs) { tree lhs_const = lhs->get_constant (); tree rhs_const = rhs->get_constant (); gcc_assert (CONSTANT_CLASS_P (lhs_const)); gcc_assert (CONSTANT_CLASS_P (rhs_const)); /* Check for comparable types. */ if (types_compatible_p (TREE_TYPE (lhs_const), TREE_TYPE (rhs_const))) { tree comparison = fold_binary (op, boolean_type_node, lhs_const, rhs_const); if (comparison == boolean_true_node) return tristate (tristate::TS_TRUE); if (comparison == boolean_false_node) return tristate (tristate::TS_FALSE); } return tristate::TS_UNKNOWN; } /* Implementation of svalue::maybe_fold_bits_within vfunc for constant_svalue. */ const svalue * constant_svalue::maybe_fold_bits_within (tree type, const bit_range &, region_model_manager *mgr) const { /* Bits within an all-zero value are also all zero. */ if (zerop (m_cst_expr)) { if (type) return mgr->get_or_create_cast (type, this); else return this; } /* Otherwise, don't fold. */ return NULL; } /* Implementation of svalue::all_zeroes_p for constant_svalue. */ bool constant_svalue::all_zeroes_p () const { return zerop (m_cst_expr); } /* class unknown_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for unknown_svalue. */ void unknown_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "UNKNOWN("); if (get_type ()) dump_tree (pp, get_type ()); pp_character (pp, ')'); } else { pp_string (pp, "unknown_svalue("); if (get_type ()) dump_tree (pp, get_type ()); pp_character (pp, ')'); } } /* Implementation of svalue::accept vfunc for unknown_svalue. */ void unknown_svalue::accept (visitor *v) const { v->visit_unknown_svalue (this); } /* Implementation of svalue::maybe_fold_bits_within vfunc for unknown_svalue. */ const svalue * unknown_svalue::maybe_fold_bits_within (tree type, const bit_range &, region_model_manager *mgr) const { /* Bits within an unknown_svalue are themselves unknown. */ return mgr->get_or_create_unknown_svalue (type); } /* Get a string for KIND for use in debug dumps. */ const char * poison_kind_to_str (enum poison_kind kind) { switch (kind) { default: gcc_unreachable (); case POISON_KIND_UNINIT: return "uninit"; case POISON_KIND_FREED: return "freed"; case POISON_KIND_POPPED_STACK: return "popped stack"; } } /* class poisoned_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for poisoned_svalue. */ void poisoned_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "POISONED("); print_quoted_type (pp, get_type ()); pp_printf (pp, ", %s)", poison_kind_to_str (m_kind)); } else { pp_string (pp, "poisoned_svalue("); print_quoted_type (pp, get_type ()); pp_printf (pp, ", %s)", poison_kind_to_str (m_kind)); } } /* Implementation of svalue::accept vfunc for poisoned_svalue. */ void poisoned_svalue::accept (visitor *v) const { v->visit_poisoned_svalue (this); } /* Implementation of svalue::maybe_fold_bits_within vfunc for poisoned_svalue. */ const svalue * poisoned_svalue::maybe_fold_bits_within (tree type, const bit_range &, region_model_manager *mgr) const { /* Bits within a poisoned value are also poisoned. */ return mgr->get_or_create_poisoned_svalue (m_kind, type); } /* class setjmp_svalue's implementation is in engine.cc, so that it can use the declaration of exploded_node. */ /* class initial_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for initial_svalue. */ void initial_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "INIT_VAL("); m_reg->dump_to_pp (pp, simple); pp_string (pp, ")"); } else { pp_string (pp, "initial_svalue("); print_quoted_type (pp, get_type ()); pp_string (pp, ", "); m_reg->dump_to_pp (pp, simple); pp_string (pp, ")"); } } /* Implementation of svalue::accept vfunc for initial_svalue. */ void initial_svalue::accept (visitor *v) const { m_reg->accept (v); v->visit_initial_svalue (this); } /* Implementation of svalue::implicitly_live_p vfunc for initial_svalue. */ bool initial_svalue::implicitly_live_p (const svalue_set *, const region_model *model) const { /* This svalue may be implicitly live if the region still implicitly has its initial value and is reachable. */ /* It must be a region that exists; we don't want to consider INIT_VAL(R) as still being implicitly reachable if R is in a popped stack frame. */ if (model->region_exists_p (m_reg)) { const svalue *reg_sval = model->get_store_value (m_reg, NULL); if (reg_sval == this) return true; } /* Assume that the initial values of params for the top level frame are still live, because (presumably) they're still live in the external caller. */ if (initial_value_of_param_p ()) if (const frame_region *frame_reg = m_reg->maybe_get_frame_region ()) if (frame_reg->get_calling_frame () == NULL) return true; return false; } /* Return true if this is the initial value of a function parameter. */ bool initial_svalue::initial_value_of_param_p () const { if (tree reg_decl = m_reg->maybe_get_decl ()) if (TREE_CODE (reg_decl) == SSA_NAME) { tree ssa_name = reg_decl; if (SSA_NAME_IS_DEFAULT_DEF (ssa_name) && SSA_NAME_VAR (ssa_name) && TREE_CODE (SSA_NAME_VAR (ssa_name)) == PARM_DECL) return true; } return false; } /* class unaryop_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for unaryop_svalue. */ void unaryop_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { if (m_op == VIEW_CONVERT_EXPR || m_op == NOP_EXPR) { pp_string (pp, "CAST("); dump_tree (pp, get_type ()); pp_string (pp, ", "); m_arg->dump_to_pp (pp, simple); pp_character (pp, ')'); } else { pp_character (pp, '('); pp_string (pp, get_tree_code_name (m_op)); //pp_string (pp, op_symbol_code (m_op)); m_arg->dump_to_pp (pp, simple); pp_character (pp, ')'); } } else { pp_string (pp, "unaryop_svalue ("); pp_string (pp, get_tree_code_name (m_op)); pp_string (pp, ", "); m_arg->dump_to_pp (pp, simple); pp_character (pp, ')'); } } /* Implementation of svalue::accept vfunc for unaryop_svalue. */ void unaryop_svalue::accept (visitor *v) const { m_arg->accept (v); v->visit_unaryop_svalue (this); } /* Implementation of svalue::implicitly_live_p vfunc for unaryop_svalue. */ bool unaryop_svalue::implicitly_live_p (const svalue_set *live_svalues, const region_model *model) const { return get_arg ()->live_p (live_svalues, model); } /* Implementation of svalue::maybe_fold_bits_within vfunc for unaryop_svalue. */ const svalue * unaryop_svalue::maybe_fold_bits_within (tree type, const bit_range &, region_model_manager *mgr) const { switch (m_op) { default: break; case NOP_EXPR: /* A cast of zero is zero. */ if (tree cst = m_arg->maybe_get_constant ()) if (zerop (cst)) { if (type) return mgr->get_or_create_cast (type, this); else return this; } break; } /* Otherwise, don't fold. */ return NULL; } /* class binop_svalue : public svalue. */ /* Return whether OP be printed as an infix operator. */ static bool infix_p (enum tree_code op) { switch (op) { default: return true; case MAX_EXPR: case MIN_EXPR: return false; } } /* Implementation of svalue::dump_to_pp vfunc for binop_svalue. */ void binop_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { if (infix_p (m_op)) { /* Print "(A OP B)". */ pp_character (pp, '('); m_arg0->dump_to_pp (pp, simple); pp_string (pp, op_symbol_code (m_op)); m_arg1->dump_to_pp (pp, simple); pp_character (pp, ')'); } else { /* Print "OP(A, B)". */ pp_string (pp, op_symbol_code (m_op)); pp_character (pp, '('); m_arg0->dump_to_pp (pp, simple); pp_string (pp, ", "); m_arg1->dump_to_pp (pp, simple); pp_character (pp, ')'); } } else { pp_string (pp, "binop_svalue ("); pp_string (pp, get_tree_code_name (m_op)); pp_string (pp, ", "); m_arg0->dump_to_pp (pp, simple); pp_string (pp, ", "); m_arg1->dump_to_pp (pp, simple); pp_character (pp, ')'); } } /* Implementation of svalue::accept vfunc for binop_svalue. */ void binop_svalue::accept (visitor *v) const { m_arg0->accept (v); m_arg1->accept (v); v->visit_binop_svalue (this); } /* Implementation of svalue::implicitly_live_p vfunc for binop_svalue. */ bool binop_svalue::implicitly_live_p (const svalue_set *live_svalues, const region_model *model) const { return (get_arg0 ()->live_p (live_svalues, model) && get_arg1 ()->live_p (live_svalues, model)); } /* class sub_svalue : public svalue. */ /* sub_svalue'c ctor. */ sub_svalue::sub_svalue (tree type, const svalue *parent_svalue, const region *subregion) : svalue (complexity::from_pair (parent_svalue->get_complexity (), subregion->get_complexity ()), type), m_parent_svalue (parent_svalue), m_subregion (subregion) { gcc_assert (parent_svalue->can_have_associated_state_p ()); } /* Implementation of svalue::dump_to_pp vfunc for sub_svalue. */ void sub_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "SUB("); m_parent_svalue->dump_to_pp (pp, simple); pp_string (pp, ", "); m_subregion->dump_to_pp (pp, simple); pp_character (pp, ')'); } else { pp_string (pp, "sub_svalue ("); pp_string (pp, ", "); m_parent_svalue->dump_to_pp (pp, simple); pp_string (pp, ", "); m_subregion->dump_to_pp (pp, simple); pp_character (pp, ')'); } } /* Implementation of svalue::accept vfunc for sub_svalue. */ void sub_svalue::accept (visitor *v) const { m_parent_svalue->accept (v); m_subregion->accept (v); v->visit_sub_svalue (this); } /* Implementation of svalue::implicitly_live_p vfunc for sub_svalue. */ bool sub_svalue::implicitly_live_p (const svalue_set *live_svalues, const region_model *model) const { return get_parent ()->live_p (live_svalues, model); } /* class repeated_svalue : public svalue. */ /* repeated_svalue'c ctor. */ repeated_svalue::repeated_svalue (tree type, const svalue *outer_size, const svalue *inner_svalue) : svalue (complexity::from_pair (outer_size, inner_svalue), type), m_outer_size (outer_size), m_inner_svalue (inner_svalue) { gcc_assert (outer_size->can_have_associated_state_p ()); gcc_assert (inner_svalue->can_have_associated_state_p ()); } /* Implementation of svalue::dump_to_pp vfunc for repeated_svalue. */ void repeated_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "REPEATED("); if (get_type ()) { print_quoted_type (pp, get_type ()); pp_string (pp, ", "); } pp_string (pp, "outer_size: "); m_outer_size->dump_to_pp (pp, simple); pp_string (pp, ", inner_val: "); m_inner_svalue->dump_to_pp (pp, simple); pp_character (pp, ')'); } else { pp_string (pp, "repeated_svalue ("); if (get_type ()) { print_quoted_type (pp, get_type ()); pp_string (pp, ", "); } pp_string (pp, "outer_size: "); m_outer_size->dump_to_pp (pp, simple); pp_string (pp, ", inner_val: "); m_inner_svalue->dump_to_pp (pp, simple); pp_character (pp, ')'); } } /* Implementation of svalue::accept vfunc for repeated_svalue. */ void repeated_svalue::accept (visitor *v) const { m_inner_svalue->accept (v); v->visit_repeated_svalue (this); } /* Implementation of svalue::all_zeroes_p for repeated_svalue. */ bool repeated_svalue::all_zeroes_p () const { return m_inner_svalue->all_zeroes_p (); } /* Implementation of svalue::maybe_fold_bits_within vfunc for repeated_svalue. */ const svalue * repeated_svalue::maybe_fold_bits_within (tree type, const bit_range &bits, region_model_manager *mgr) const { const svalue *innermost_sval = m_inner_svalue; /* Fold BITS_WITHIN (range, REPEATED_SVALUE (ZERO)) to: REPEATED_SVALUE (ZERO). */ if (all_zeroes_p ()) { byte_range bytes (0,0); if (bits.as_byte_range (&bytes)) { const svalue *byte_size = mgr->get_or_create_int_cst (size_type_node, bytes.m_size_in_bytes.to_uhwi ()); return mgr->get_or_create_repeated_svalue (type, byte_size, innermost_sval); } } /* Fold: BITS_WITHIN (range, REPEATED_SVALUE (INNERMOST_SVALUE)) to: BITS_WITHIN (range - offset, INNERMOST_SVALUE) if range is fully within one instance of INNERMOST_SVALUE. */ if (tree innermost_type = innermost_sval->get_type ()) { bit_size_t element_bit_size; if (int_size_in_bits (innermost_type, &element_bit_size) && element_bit_size > 0) { HOST_WIDE_INT start_idx = (bits.get_start_bit_offset () / element_bit_size).to_shwi (); HOST_WIDE_INT last_idx = (bits.get_last_bit_offset () / element_bit_size).to_shwi (); if (start_idx == last_idx) { bit_offset_t start_of_element = start_idx * element_bit_size; bit_range range_within_element (bits.m_start_bit_offset - start_of_element, bits.m_size_in_bits); return mgr->get_or_create_bits_within (type, range_within_element, innermost_sval); } } } return NULL; } /* class bits_within_svalue : public svalue. */ /* bits_within_svalue'c ctor. */ bits_within_svalue::bits_within_svalue (tree type, const bit_range &bits, const svalue *inner_svalue) : svalue (complexity (inner_svalue), type), m_bits (bits), m_inner_svalue (inner_svalue) { gcc_assert (inner_svalue->can_have_associated_state_p ()); } /* Implementation of svalue::dump_to_pp vfunc for bits_within_svalue. */ void bits_within_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "BITS_WITHIN("); if (get_type ()) { print_quoted_type (pp, get_type ()); pp_string (pp, ", "); } m_bits.dump_to_pp (pp); pp_string (pp, ", inner_val: "); m_inner_svalue->dump_to_pp (pp, simple); pp_character (pp, ')'); } else { pp_string (pp, "bits_within_svalue ("); if (get_type ()) { print_quoted_type (pp, get_type ()); pp_string (pp, ", "); } m_bits.dump_to_pp (pp); pp_string (pp, ", inner_val: "); m_inner_svalue->dump_to_pp (pp, simple); pp_character (pp, ')'); } } /* Implementation of svalue::maybe_fold_bits_within vfunc for bits_within_svalue. */ const svalue * bits_within_svalue::maybe_fold_bits_within (tree type, const bit_range &bits, region_model_manager *mgr) const { /* Fold: BITS_WITHIN (range1, BITS_WITHIN (range2, VAL)) to: BITS_WITHIN (range1 in range 2, VAL). */ bit_range offset_bits (m_bits.get_start_bit_offset () + bits.m_start_bit_offset, bits.m_size_in_bits); return mgr->get_or_create_bits_within (type, offset_bits, m_inner_svalue); } /* Implementation of svalue::accept vfunc for bits_within_svalue. */ void bits_within_svalue::accept (visitor *v) const { m_inner_svalue->accept (v); v->visit_bits_within_svalue (this); } /* Implementation of svalue::implicitly_live_p vfunc for bits_within_svalue. */ bool bits_within_svalue::implicitly_live_p (const svalue_set *live_svalues, const region_model *model) const { return m_inner_svalue->live_p (live_svalues, model); } /* class widening_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for widening_svalue. */ void widening_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "WIDENING("); pp_character (pp, '{'); m_point.print (pp, format (false)); pp_string (pp, "}, "); m_base_sval->dump_to_pp (pp, simple); pp_string (pp, ", "); m_iter_sval->dump_to_pp (pp, simple); pp_character (pp, ')'); } else { pp_string (pp, "widening_svalue ("); pp_string (pp, ", "); pp_character (pp, '{'); m_point.print (pp, format (false)); pp_string (pp, "}, "); m_base_sval->dump_to_pp (pp, simple); pp_string (pp, ", "); m_iter_sval->dump_to_pp (pp, simple); pp_character (pp, ')'); } } /* Implementation of svalue::accept vfunc for widening_svalue. */ void widening_svalue::accept (visitor *v) const { m_base_sval->accept (v); m_iter_sval->accept (v); v->visit_widening_svalue (this); } /* Attempt to determine in which direction this value is changing w.r.t. the initial value. */ enum widening_svalue::direction_t widening_svalue::get_direction () const { tree base_cst = m_base_sval->maybe_get_constant (); if (base_cst == NULL_TREE) return DIR_UNKNOWN; tree iter_cst = m_iter_sval->maybe_get_constant (); if (iter_cst == NULL_TREE) return DIR_UNKNOWN; tree iter_gt_base = fold_binary (GT_EXPR, boolean_type_node, iter_cst, base_cst); if (iter_gt_base == boolean_true_node) return DIR_ASCENDING; tree iter_lt_base = fold_binary (LT_EXPR, boolean_type_node, iter_cst, base_cst); if (iter_lt_base == boolean_true_node) return DIR_DESCENDING; return DIR_UNKNOWN; } /* Compare this value against constant RHS_CST. */ tristate widening_svalue::eval_condition_without_cm (enum tree_code op, tree rhs_cst) const { tree base_cst = m_base_sval->maybe_get_constant (); if (base_cst == NULL_TREE) return tristate::TS_UNKNOWN; tree iter_cst = m_iter_sval->maybe_get_constant (); if (iter_cst == NULL_TREE) return tristate::TS_UNKNOWN; switch (get_direction ()) { default: gcc_unreachable (); case DIR_ASCENDING: /* LHS is in [base_cst, +ve infinity), assuming no overflow. */ switch (op) { case LE_EXPR: case LT_EXPR: { /* [BASE, +INF) OP RHS: This is either true or false at +ve ininity, It can be true for points X where X OP RHS, so we have either "false", or "unknown". */ tree base_op_rhs = fold_binary (op, boolean_type_node, base_cst, rhs_cst); if (base_op_rhs == boolean_true_node) return tristate::TS_UNKNOWN; else return tristate::TS_FALSE; } case GE_EXPR: case GT_EXPR: { /* [BASE, +INF) OP RHS: This is true at +ve infinity. It will be true everywhere in the range if BASE >= RHS. */ tree base_op_rhs = fold_binary (op, boolean_type_node, base_cst, rhs_cst); if (base_op_rhs == boolean_true_node) return tristate::TS_TRUE; else return tristate::TS_UNKNOWN; } case EQ_EXPR: { /* [BASE, +INF) == RHS: Could this be true at any point in the range? If so we have "unknown", otherwise we have "false". */ tree base_le_rhs = fold_binary (LE_EXPR, boolean_type_node, base_cst, rhs_cst); if (base_le_rhs == boolean_true_node) return tristate::TS_UNKNOWN; else return tristate::TS_FALSE; } case NE_EXPR: { /* [BASE, +INF) != RHS: Could we have equality at any point in the range? If so we have "unknown", otherwise we have "true". */ tree base_le_rhs = fold_binary (LE_EXPR, boolean_type_node, base_cst, rhs_cst); if (base_le_rhs == boolean_true_node) return tristate::TS_UNKNOWN; else return tristate::TS_TRUE; } default: return tristate::TS_UNKNOWN; } case DIR_DESCENDING: /* LHS is in (-ve infinity, base_cst], assuming no overflow. */ return tristate::TS_UNKNOWN; case DIR_UNKNOWN: return tristate::TS_UNKNOWN; } } /* class placeholder_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for placeholder_svalue. */ void placeholder_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) pp_printf (pp, "PLACEHOLDER(%qs)", m_name); else pp_printf (pp, "placeholder_svalue (%qs)", m_name); } /* Implementation of svalue::accept vfunc for placeholder_svalue. */ void placeholder_svalue::accept (visitor *v) const { v->visit_placeholder_svalue (this); } /* class unmergeable_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for unmergeable_svalue. */ void unmergeable_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "UNMERGEABLE("); m_arg->dump_to_pp (pp, simple); pp_character (pp, ')'); } else { pp_string (pp, "unmergeable_svalue ("); m_arg->dump_to_pp (pp, simple); pp_character (pp, ')'); } } /* Implementation of svalue::accept vfunc for unmergeable_svalue. */ void unmergeable_svalue::accept (visitor *v) const { m_arg->accept (v); v->visit_unmergeable_svalue (this); } /* Implementation of svalue::implicitly_live_p vfunc for unmergeable_svalue. */ bool unmergeable_svalue::implicitly_live_p (const svalue_set *live_svalues, const region_model *model) const { return get_arg ()->live_p (live_svalues, model); } /* class compound_svalue : public svalue. */ compound_svalue::compound_svalue (tree type, const binding_map &map) : svalue (calc_complexity (map), type), m_map (map) { /* All keys within the underlying binding_map are required to be concrete, not symbolic. */ #if CHECKING_P for (iterator_t iter = begin (); iter != end (); ++iter) { const binding_key *key = (*iter).first; gcc_assert (key->concrete_p ()); } #endif } /* Implementation of svalue::dump_to_pp vfunc for compound_svalue. */ void compound_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "COMPOUND("); if (get_type ()) { print_quoted_type (pp, get_type ()); pp_string (pp, ", "); } pp_character (pp, '{'); m_map.dump_to_pp (pp, simple, false); pp_string (pp, "})"); } else { pp_string (pp, "compound_svalue ("); if (get_type ()) { print_quoted_type (pp, get_type ()); pp_string (pp, ", "); } pp_character (pp, '{'); m_map.dump_to_pp (pp, simple, false); pp_string (pp, "})"); } } /* Implementation of svalue::accept vfunc for compound_svalue. */ void compound_svalue::accept (visitor *v) const { for (binding_map::iterator_t iter = m_map.begin (); iter != m_map.end (); ++iter) { //(*iter).first.accept (v); (*iter).second->accept (v); } v->visit_compound_svalue (this); } /* Calculate what the complexity of a compound_svalue instance for MAP will be, based on the svalues bound within MAP. */ complexity compound_svalue::calc_complexity (const binding_map &map) { unsigned num_child_nodes = 0; unsigned max_child_depth = 0; for (binding_map::iterator_t iter = map.begin (); iter != map.end (); ++iter) { const complexity &sval_c = (*iter).second->get_complexity (); num_child_nodes += sval_c.m_num_nodes; max_child_depth = MAX (max_child_depth, sval_c.m_max_depth); } return complexity (num_child_nodes + 1, max_child_depth + 1); } /* Implementation of svalue::maybe_fold_bits_within vfunc for compound_svalue. */ const svalue * compound_svalue::maybe_fold_bits_within (tree type, const bit_range &bits, region_model_manager *mgr) const { binding_map result_map; for (auto iter : m_map) { const binding_key *key = iter.first; if (const concrete_binding *conc_key = key->dyn_cast_concrete_binding ()) { /* Ignore concrete bindings outside BITS. */ if (!conc_key->get_bit_range ().intersects_p (bits)) continue; const svalue *sval = iter.second; /* Get the position of conc_key relative to BITS. */ bit_range result_location (conc_key->get_start_bit_offset () - bits.get_start_bit_offset (), conc_key->get_size_in_bits ()); /* If conc_key starts after BITS, trim off leading bits from the svalue and adjust binding location. */ if (result_location.m_start_bit_offset < 0) { bit_size_t leading_bits_to_drop = -result_location.m_start_bit_offset; result_location = bit_range (0, result_location.m_size_in_bits - leading_bits_to_drop); bit_range bits_within_sval (leading_bits_to_drop, result_location.m_size_in_bits); /* Trim off leading bits from iter_sval. */ sval = mgr->get_or_create_bits_within (NULL_TREE, bits_within_sval, sval); } /* If conc_key finishes after BITS, trim off trailing bits from the svalue and adjust binding location. */ if (conc_key->get_next_bit_offset () > bits.get_next_bit_offset ()) { bit_size_t trailing_bits_to_drop = (conc_key->get_next_bit_offset () - bits.get_next_bit_offset ()); result_location = bit_range (result_location.m_start_bit_offset, result_location.m_size_in_bits - trailing_bits_to_drop); bit_range bits_within_sval (0, result_location.m_size_in_bits); /* Trim off leading bits from iter_sval. */ sval = mgr->get_or_create_bits_within (NULL_TREE, bits_within_sval, sval); } const concrete_binding *offset_conc_key = mgr->get_store_manager ()->get_concrete_binding (result_location); result_map.put (offset_conc_key, sval); } else /* If we have any symbolic keys we can't get it as bits. */ return NULL; } return mgr->get_or_create_compound_svalue (type, result_map); } /* class conjured_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for conjured_svalue. */ void conjured_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_string (pp, "CONJURED("); pp_gimple_stmt_1 (pp, m_stmt, 0, (dump_flags_t)0); pp_string (pp, ", "); m_id_reg->dump_to_pp (pp, simple); pp_character (pp, ')'); } else { pp_string (pp, "conjured_svalue ("); pp_string (pp, ", "); pp_gimple_stmt_1 (pp, m_stmt, 0, (dump_flags_t)0); pp_string (pp, ", "); m_id_reg->dump_to_pp (pp, simple); pp_character (pp, ')'); } } /* Implementation of svalue::accept vfunc for conjured_svalue. */ void conjured_svalue::accept (visitor *v) const { m_id_reg->accept (v); v->visit_conjured_svalue (this); } /* class asm_output_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for asm_output_svalue. */ void asm_output_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_printf (pp, "ASM_OUTPUT(%qs, %%%i, {", get_asm_string (), get_output_idx ()); for (unsigned i = 0; i < m_num_inputs; i++) { if (i > 0) pp_string (pp, ", "); dump_input (pp, 0, m_input_arr[i], simple); } pp_string (pp, "})"); } else { pp_printf (pp, "asm_output_svalue (%qs, %%%i, {", get_asm_string (), get_output_idx ()); for (unsigned i = 0; i < m_num_inputs; i++) { if (i > 0) pp_string (pp, ", "); dump_input (pp, 0, m_input_arr[i], simple); } pp_string (pp, "})"); } } /* Subroutine of asm_output_svalue::dump_to_pp. */ void asm_output_svalue::dump_input (pretty_printer *pp, unsigned input_idx, const svalue *sval, bool simple) const { pp_printf (pp, "%%%i: ", input_idx_to_asm_idx (input_idx)); sval->dump_to_pp (pp, simple); } /* Convert INPUT_IDX from an index into the array of inputs into the index of all operands for the asm stmt. */ unsigned asm_output_svalue::input_idx_to_asm_idx (unsigned input_idx) const { return input_idx + m_num_outputs; } /* Implementation of svalue::accept vfunc for asm_output_svalue. */ void asm_output_svalue::accept (visitor *v) const { for (unsigned i = 0; i < m_num_inputs; i++) m_input_arr[i]->accept (v); v->visit_asm_output_svalue (this); } /* class const_fn_result_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for const_fn_result_svalue. */ void const_fn_result_svalue::dump_to_pp (pretty_printer *pp, bool simple) const { if (simple) { pp_printf (pp, "CONST_FN_RESULT(%qD, {", m_fndecl); for (unsigned i = 0; i < m_num_inputs; i++) { if (i > 0) pp_string (pp, ", "); dump_input (pp, i, m_input_arr[i], simple); } pp_string (pp, "})"); } else { pp_printf (pp, "CONST_FN_RESULT(%qD, {", m_fndecl); for (unsigned i = 0; i < m_num_inputs; i++) { if (i > 0) pp_string (pp, ", "); dump_input (pp, i, m_input_arr[i], simple); } pp_string (pp, "})"); } } /* Subroutine of const_fn_result_svalue::dump_to_pp. */ void const_fn_result_svalue::dump_input (pretty_printer *pp, unsigned input_idx, const svalue *sval, bool simple) const { pp_printf (pp, "arg%i: ", input_idx); sval->dump_to_pp (pp, simple); } /* Implementation of svalue::accept vfunc for const_fn_result_svalue. */ void const_fn_result_svalue::accept (visitor *v) const { for (unsigned i = 0; i < m_num_inputs; i++) m_input_arr[i]->accept (v); v->visit_const_fn_result_svalue (this); } } // namespace ana #endif /* #if ENABLE_ANALYZER */