/* Support routines for value queries. Copyright (C) 2020-2021 Free Software Foundation, Inc. Contributed by Aldy Hernandez and Andrew MacLeod . 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 "backend.h" #include "tree.h" #include "gimple.h" #include "ssa.h" #include "tree-pretty-print.h" #include "fold-const.h" #include "value-range-equiv.h" #include "value-query.h" #include "alloc-pool.h" #include "gimple-range.h" // value_query default methods. tree value_query::value_on_edge (edge, tree name) { return value_of_expr (name); } tree value_query::value_of_stmt (gimple *stmt, tree name) { if (!name) name = gimple_get_lhs (stmt); gcc_checking_assert (!name || name == gimple_get_lhs (stmt)); if (name) return value_of_expr (name); return NULL_TREE; } // range_query default methods. bool range_query::range_on_edge (irange &r, edge, tree name) { return range_of_expr (r, name); } bool range_query::range_of_stmt (irange &r, gimple *stmt, tree name) { if (!name) name = gimple_get_lhs (stmt); gcc_checking_assert (!name || name == gimple_get_lhs (stmt)); if (name) return range_of_expr (r, name); return false; } tree range_query::value_of_expr (tree name, gimple *stmt) { tree t; int_range_max r; if (!irange::supports_type_p (TREE_TYPE (name))) return NULL_TREE; if (range_of_expr (r, name, stmt)) { // A constant used in an unreachable block oftens returns as UNDEFINED. // If the result is undefined, check the global value for a constant. if (r.undefined_p ()) range_of_expr (r, name); if (r.singleton_p (&t)) return t; } return NULL_TREE; } tree range_query::value_on_edge (edge e, tree name) { tree t; int_range_max r; if (!irange::supports_type_p (TREE_TYPE (name))) return NULL_TREE; if (range_on_edge (r, e, name)) { // A constant used in an unreachable block oftens returns as UNDEFINED. // If the result is undefined, check the global value for a constant. if (r.undefined_p ()) range_of_expr (r, name); if (r.singleton_p (&t)) return t; } return NULL_TREE; } tree range_query::value_of_stmt (gimple *stmt, tree name) { tree t; int_range_max r; if (!name) name = gimple_get_lhs (stmt); gcc_checking_assert (!name || name == gimple_get_lhs (stmt)); if (!name || !irange::supports_type_p (TREE_TYPE (name))) return NULL_TREE; if (range_of_stmt (r, stmt, name) && r.singleton_p (&t)) return t; return NULL_TREE; } void range_query::dump (FILE *) { } // valuation_query support routines for value_range_equiv's. class equiv_allocator : public object_allocator { public: equiv_allocator () : object_allocator ("equiv_allocator pool") { } }; value_range_equiv * range_query::allocate_value_range_equiv () { return new (equiv_alloc->allocate ()) value_range_equiv; } void range_query::free_value_range_equiv (value_range_equiv *v) { equiv_alloc->remove (v); } const class value_range_equiv * range_query::get_value_range (const_tree expr, gimple *stmt) { int_range_max r; if (range_of_expr (r, const_cast (expr), stmt)) return new (equiv_alloc->allocate ()) value_range_equiv (r); return new (equiv_alloc->allocate ()) value_range_equiv (TREE_TYPE (expr)); } range_query::range_query () { equiv_alloc = new equiv_allocator; } range_query::~range_query () { equiv_alloc->release (); delete equiv_alloc; } // Return the range for NAME from SSA_NAME_RANGE_INFO. static inline void get_ssa_name_range_info (irange &r, const_tree name) { tree type = TREE_TYPE (name); gcc_checking_assert (!POINTER_TYPE_P (type)); gcc_checking_assert (TREE_CODE (name) == SSA_NAME); range_info_def *ri = SSA_NAME_RANGE_INFO (name); // Return VR_VARYING for SSA_NAMEs with NULL RANGE_INFO or SSA_NAMEs // with integral types width > 2 * HOST_BITS_PER_WIDE_INT precision. if (!ri || (GET_MODE_PRECISION (SCALAR_INT_TYPE_MODE (TREE_TYPE (name))) > 2 * HOST_BITS_PER_WIDE_INT)) r.set_varying (type); else r.set (wide_int_to_tree (type, ri->get_min ()), wide_int_to_tree (type, ri->get_max ()), SSA_NAME_RANGE_TYPE (name)); } // Return nonnull attribute of pointer NAME from SSA_NAME_PTR_INFO. static inline bool get_ssa_name_ptr_info_nonnull (const_tree name) { gcc_assert (POINTER_TYPE_P (TREE_TYPE (name))); struct ptr_info_def *pi = SSA_NAME_PTR_INFO (name); if (pi == NULL) return false; /* TODO Now pt->null is conservatively set to true in PTA analysis. vrp is the only pass (including ipa-vrp) that clears pt.null via set_ptr_nonull when it knows for sure. PTA will preserves the pt.null value set by VRP. When PTA analysis is improved, pt.anything, pt.nonlocal and pt.escaped may also has to be considered before deciding that pointer cannot point to NULL. */ return !pi->pt.null; } // Return the legacy global range for NAME if it has one, otherwise // return VARYING. static void get_range_global (irange &r, tree name) { tree type = TREE_TYPE (name); if (SSA_NAME_IS_DEFAULT_DEF (name)) { tree sym = SSA_NAME_VAR (name); // Adapted from vr_values::get_lattice_entry(). // Use a range from an SSA_NAME's available range. if (TREE_CODE (sym) == PARM_DECL) { // Try to use the "nonnull" attribute to create ~[0, 0] // anti-ranges for pointers. Note that this is only valid with // default definitions of PARM_DECLs. if (POINTER_TYPE_P (type) && ((cfun && nonnull_arg_p (sym)) || get_ssa_name_ptr_info_nonnull (name))) r.set_nonzero (type); else if (INTEGRAL_TYPE_P (type)) { get_ssa_name_range_info (r, name); if (r.undefined_p ()) r.set_varying (type); } else r.set_varying (type); } // If this is a local automatic with no definition, use undefined. else if (TREE_CODE (sym) != RESULT_DECL) r.set_undefined (); else r.set_varying (type); } else if (!POINTER_TYPE_P (type) && SSA_NAME_RANGE_INFO (name)) { get_ssa_name_range_info (r, name); if (r.undefined_p ()) r.set_varying (type); } else if (POINTER_TYPE_P (type) && SSA_NAME_PTR_INFO (name)) { if (get_ssa_name_ptr_info_nonnull (name)) r.set_nonzero (type); else r.set_varying (type); } else r.set_varying (type); } // ?? Like above, but only for default definitions of NAME. This is // so VRP passes using ranger do not start with known ranges, // otherwise we'd eliminate builtin_unreachables too early because of // inlining. // // Without this restriction, the test in g++.dg/tree-ssa/pr61034.C has // all of its unreachable calls removed too early. We should // investigate whether we should just adjust the test above. value_range gimple_range_global (tree name) { gcc_checking_assert (gimple_range_ssa_p (name)); tree type = TREE_TYPE (name); if (SSA_NAME_IS_DEFAULT_DEF (name)) { value_range vr; get_range_global (vr, name); return vr; } return value_range (type); } // ---------------------------------------------- // global_range_query implementation. global_range_query global_ranges; // Like get_range_query, but for accessing global ranges. range_query * get_global_range_query () { return &global_ranges; } bool global_range_query::range_of_expr (irange &r, tree expr, gimple *) { tree type = TREE_TYPE (expr); if (!irange::supports_type_p (type) || !gimple_range_ssa_p (expr)) return get_tree_range (r, expr); get_range_global (r, expr); return true; }