| /* 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/>. */ |
| |
| #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.m_buffer); |
| desc.maybe_free (); |
| 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<svalue_set *> (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<const svalue *>::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) |
| { |
| if (candidate == m_needle) |
| m_found = true; |
| } |
| |
| void visit_conjured_svalue (const conjured_svalue *candidate) |
| { |
| 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 <const region_svalue *> (iter); |
| return region_sval->get_pointee ()->get_base_region (); |
| } |
| |
| case SK_BINOP: |
| { |
| const binop_svalue *binop_sval |
| = as_a <const binop_svalue *> (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 |
| { |
| v->visit_region_svalue (this); |
| m_reg->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_initial_svalue (this); |
| m_reg->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_unaryop_svalue (this); |
| m_arg->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_binop_svalue (this); |
| m_arg0->accept (v); |
| m_arg1->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_sub_svalue (this); |
| m_parent_svalue->accept (v); |
| m_subregion->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_repeated_svalue (this); |
| m_inner_svalue->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_bits_within_svalue (this); |
| m_inner_svalue->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_widening_svalue (this); |
| m_base_sval->accept (v); |
| m_iter_sval->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_unmergeable_svalue (this); |
| m_arg->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_compound_svalue (this); |
| for (binding_map::iterator_t iter = m_map.begin (); |
| iter != m_map.end (); ++iter) |
| { |
| //(*iter).first.accept (v); |
| (*iter).second->accept (v); |
| } |
| } |
| |
| /* 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 |
| { |
| v->visit_conjured_svalue (this); |
| m_id_reg->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_asm_output_svalue (this); |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| m_input_arr[i]->accept (v); |
| } |
| |
| /* 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 |
| { |
| v->visit_const_fn_result_svalue (this); |
| for (unsigned i = 0; i < m_num_inputs; i++) |
| m_input_arr[i]->accept (v); |
| } |
| |
| } // namespace ana |
| |
| #endif /* #if ENABLE_ANALYZER */ |