From: Andrew MacLeod Date: Wed, 19 May 2021 00:33:09 +0000 (-0400) Subject: Split gimple range folding with ranges into a stand alone class. X-Git-Tag: upstream/12.2.0~7768 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dc6758f03effbf7d6946d8c314576c7a6c0003af;p=platform%2Fupstream%2Fgcc.git Split gimple range folding with ranges into a stand alone class. Introduces fold_using_range which folds any kind of gimple statement by querying argument ranges thru a generic range_query. This pulls all the statement processing into a client neutral location. * gimple-range.cc (fur_source::get_operand): New. (gimple_range_fold): Delete. (fold_using_range::fold_stmt): Move from gimple_ranger::calc_stmt. (fold_using_range::range_of_range_op): Move from gimple_ranger. (fold_using_range::range_of_address): Ditto. (fold_using_range::range_of_phi): Ditto. (fold_using_range::range_of_call): Ditto. (fold_using_range::range_of_builtin_ubsan_call): Move from range_of_builtin_ubsan_call. (fold_using_range::range_of_builtin_call): Move from range_of_builtin_call. (gimple_ranger::range_of_builtin_call): Delete. (fold_using_range::range_of_cond_expr): Move from gimple_ranger. (gimple_ranger::fold_range_internal): New. (gimple_ranger::range_of_stmt): Use new fold_using_range API. (fold_using_range::range_of_ssa_name_with_loop_info): Move from gimple_ranger. Improve ranges of SSA_NAMES when possible. * gimple-range.h (gimple_ranger): Remove various range_of routines. (class fur_source): New. (class fold_using_range): New. (fur_source::fur_source): New. (fold_range): New. * vr-values.c (vr_values::extract_range_basic): Use fold_using_range instead of range_of_builtin_call. --- diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc index 710bc7f..06e9804 100644 --- a/gcc/gimple-range.cc +++ b/gcc/gimple-range.cc @@ -47,6 +47,31 @@ along with GCC; see the file COPYING3. If not see #include "vr-values.h" #include "gimple-range.h" +// Evaluate expression EXPR using the source information the class was +// instantiated with. Place the result in R, and return TRUE. If a range +// cannot be calcluated, return FALSE. + +bool +fur_source::get_operand (irange &r, tree expr) +{ + if (!gimple_range_ssa_p (expr)) + return get_tree_range (r, expr); + + // If no query engine is present, simply get the global value. + if (!m_query) + { + r = gimple_range_global (expr); + return true; + } + + // First look for a stmt. + if (m_stmt) + return m_query->range_of_expr (r, expr, m_stmt); + + // Finally must be on an edge. + return m_query->range_on_edge (r, m_edge, expr); +} + // Adjust the range for a pointer difference where the operands came // from a memchr. @@ -193,41 +218,9 @@ get_tree_range (irange &r, tree expr) return true; } -// Fold this unary statement using R1 as operand1's range, returning -// the result in RES. Return false if the operation fails. - -bool -gimple_range_fold (irange &res, const gimple *stmt, const irange &r1) -{ - gcc_checking_assert (gimple_range_handler (stmt)); - - tree type = gimple_expr_type (stmt); - // Unary SSA operations require the LHS type as the second range. - int_range<2> r2 (type); - - return gimple_range_fold (res, stmt, r1, r2); -} - -// Fold this binary statement using R1 and R2 as the operands ranges, -// returning the result in RES. Return false if the operation fails. - -bool -gimple_range_fold (irange &res, const gimple *stmt, - const irange &r1, const irange &r2) -{ - gcc_checking_assert (gimple_range_handler (stmt)); - - gimple_range_handler (stmt)->fold_range (res, gimple_expr_type (stmt), - r1, r2); - - // If there are any gimple lookups, do those now. - gimple_range_adjustment (res, stmt); - return true; -} - // Return the base of the RHS of an assignment. -tree +static tree gimple_range_base_of_assignment (const gimple *stmt) { gcc_checking_assert (gimple_code (stmt) == GIMPLE_ASSIGN); @@ -364,20 +357,29 @@ gimple_range_calc_op2 (irange &r, const gimple *stmt, // be calculated, return false. bool -gimple_ranger::calc_stmt (irange &r, gimple *s, tree name) +fold_using_range::fold_stmt (irange &r, gimple *s, fur_source &src, tree name) { bool res = false; - // If name is specified, make sure it is an LHS of S. - gcc_checking_assert (name ? SSA_NAME_DEF_STMT (name) == s : true); + // If name and S are specified, make sure it is an LHS of S. + gcc_checking_assert (!name || !gimple_get_lhs (s) || + name == gimple_get_lhs (s)); + + if (!name) + name = gimple_get_lhs (s); + + // Process addresses. + if (gimple_code (s) == GIMPLE_ASSIGN + && gimple_assign_rhs_code (s) == ADDR_EXPR) + return range_of_address (r, s, src); if (gimple_range_handler (s)) - res = range_of_range_op (r, s); + res = range_of_range_op (r, s, src); else if (is_a(s)) - res = range_of_phi (r, as_a (s)); + res = range_of_phi (r, as_a (s), src); else if (is_a(s)) - res = range_of_call (r, as_a (s)); + res = range_of_call (r, as_a (s), src); else if (is_a (s) && gimple_assign_rhs_code (s) == COND_EXPR) - res = range_of_cond_expr (r, as_a (s)); + res = range_of_cond_expr (r, as_a (s), src); if (!res) { @@ -414,36 +416,45 @@ gimple_ranger::calc_stmt (irange &r, gimple *s, tree name) // If a range cannot be calculated, return false. bool -gimple_ranger::range_of_range_op (irange &r, gimple *s) +fold_using_range::range_of_range_op (irange &r, gimple *s, fur_source &src) { int_range_max range1, range2; - tree lhs = gimple_get_lhs (s); tree type = gimple_expr_type (s); + range_operator *handler = gimple_range_handler (s); + gcc_checking_assert (handler); gcc_checking_assert (irange::supports_type_p (type)); + tree lhs = gimple_get_lhs (s); tree op1 = gimple_range_operand1 (s); tree op2 = gimple_range_operand2 (s); - if (lhs) - { - // Register potential dependencies for stale value tracking. - m_cache.register_dependency (lhs, op1); - m_cache.register_dependency (lhs, op2); - } - - if (gimple_code (s) == GIMPLE_ASSIGN - && gimple_assign_rhs_code (s) == ADDR_EXPR) - return range_of_address (r, s); - - if (range_of_expr (range1, op1, s)) + if (src.get_operand (range1, op1)) { if (!op2) - return gimple_range_fold (r, s, range1); - - if (range_of_expr (range2, op2, s)) - return gimple_range_fold (r, s, range1, range2); + { + // Fold range, and register any dependency if available. + int_range<2> r2 (type); + handler->fold_range (r, type, range1, r2); + if (lhs && src.m_cache) + src.m_cache->register_dependency (lhs, op1); + } + else if (src.get_operand (range2, op2)) + { + // Fold range, and register any dependency if available. + handler->fold_range (r, type, range1, range2); + if (lhs && src.m_cache) + { + src.m_cache->register_dependency (lhs, op1); + src.m_cache->register_dependency (lhs, op2); + } + } + else + r.set_varying (type); } - r.set_varying (type); + else + r.set_varying (type); + // Make certain range-op adjustments that aren't handled any other way. + gimple_range_adjustment (r, s); return true; } @@ -452,7 +463,7 @@ gimple_ranger::range_of_range_op (irange &r, gimple *s) // If a range cannot be calculated, set it to VARYING and return true. bool -gimple_ranger::range_of_address (irange &r, gimple *stmt) +fold_using_range::range_of_address (irange &r, gimple *stmt, fur_source &src) { gcc_checking_assert (gimple_code (stmt) == GIMPLE_ASSIGN); gcc_checking_assert (gimple_assign_rhs_code (stmt) == ADDR_EXPR); @@ -473,8 +484,11 @@ gimple_ranger::range_of_address (irange &r, gimple *stmt) && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME) { tree ssa = TREE_OPERAND (base, 0); + tree lhs = gimple_get_lhs (stmt); + if (src.m_cache && lhs && gimple_range_ssa_p (ssa)) + src.m_cache->register_dependency (lhs, ssa); gcc_checking_assert (irange::supports_type_p (TREE_TYPE (ssa))); - range_of_expr (r, ssa, stmt); + src.get_operand (r, ssa); range_cast (r, TREE_TYPE (gimple_assign_rhs1 (stmt))); poly_offset_int off = 0; @@ -531,7 +545,7 @@ gimple_ranger::range_of_address (irange &r, gimple *stmt) // If a range cannot be calculated, return false. bool -gimple_ranger::range_of_phi (irange &r, gphi *phi) +fold_using_range::range_of_phi (irange &r, gphi *phi, fur_source &src) { tree phi_def = gimple_phi_result (phi); tree type = TREE_TYPE (phi_def); @@ -549,9 +563,19 @@ gimple_ranger::range_of_phi (irange &r, gphi *phi) edge e = gimple_phi_arg_edge (phi, x); // Register potential dependencies for stale value tracking. - m_cache.register_dependency (phi_def, arg); - - range_on_edge (arg_range, e, arg); + if (src.m_cache && gimple_range_ssa_p (arg)) + src.m_cache->register_dependency (phi_def, arg); + + // Get the range of the argument on its edge. + fur_source e_src (src.m_query, e); + e_src.get_operand (arg_range, arg); + // If we're recomputing the argument elsewhere, try to refine it. + if (src.m_stmt != phi) + { + int_range_max tmp; + e_src.get_operand (tmp, arg); + arg_range.intersect (tmp); + } r.union_ (arg_range); // Once the value reaches varying, stop looking. if (r.varying_p ()) @@ -565,7 +589,7 @@ gimple_ranger::range_of_phi (irange &r, gphi *phi) class loop *l = loop_containing_stmt (phi); if (l && loop_outer (l)) { - range_of_ssa_name_with_loop_info (loop_range, phi_def, l, phi); + range_of_ssa_name_with_loop_info (loop_range, phi_def, l, phi, src); if (!loop_range.varying_p ()) { if (dump_file && (dump_flags & TDF_DETAILS)) @@ -590,7 +614,7 @@ gimple_ranger::range_of_phi (irange &r, gphi *phi) // If a range cannot be calculated, return false. bool -gimple_ranger::range_of_call (irange &r, gcall *call) +fold_using_range::range_of_call (irange &r, gcall *call, fur_source &src) { tree type = gimple_call_return_type (call); tree lhs = gimple_call_lhs (call); @@ -599,7 +623,7 @@ gimple_ranger::range_of_call (irange &r, gcall *call) if (!irange::supports_type_p (type)) return false; - if (range_of_builtin_call (r, call)) + if (range_of_builtin_call (r, call, src)) ; else if (gimple_stmt_nonnegative_warnv_p (call, &strict_overflow_p)) r.set (build_int_cst (type, 0), TYPE_MAX_VALUE (type)); @@ -623,9 +647,9 @@ gimple_ranger::range_of_call (irange &r, gcall *call) // CODE is the type of ubsan call (PLUS_EXPR, MINUS_EXPR or // MULT_EXPR). -static void -range_of_builtin_ubsan_call (range_query &query, irange &r, gcall *call, - tree_code code) +void +fold_using_range::range_of_builtin_ubsan_call (irange &r, gcall *call, + tree_code code, fur_source &src) { gcc_checking_assert (code == PLUS_EXPR || code == MINUS_EXPR || code == MULT_EXPR); @@ -635,8 +659,8 @@ range_of_builtin_ubsan_call (range_query &query, irange &r, gcall *call, int_range_max ir0, ir1; tree arg0 = gimple_call_arg (call, 0); tree arg1 = gimple_call_arg (call, 1); - query.range_of_expr (ir0, arg0, call); - query.range_of_expr (ir1, arg1, call); + src.get_operand (ir0, arg0); + src.get_operand (ir1, arg1); bool saved_flag_wrapv = flag_wrapv; // Pretend the arithmetic is wrapping. If there is any overflow, @@ -656,7 +680,8 @@ range_of_builtin_ubsan_call (range_query &query, irange &r, gcall *call, // TRUE. Otherwise return FALSE. bool -range_of_builtin_call (range_query &query, irange &r, gcall *call) +fold_using_range::range_of_builtin_call (irange &r, gcall *call, + fur_source &src) { combined_fn func = gimple_call_combined_fn (call); if (func == CFN_LAST) @@ -677,7 +702,7 @@ range_of_builtin_call (range_query &query, irange &r, gcall *call) return true; } arg = gimple_call_arg (call, 0); - if (query.range_of_expr (r, arg, call) && r.singleton_p ()) + if (src.get_operand (r, arg) && r.singleton_p ()) { r.set (build_one_cst (type), build_one_cst (type)); return true; @@ -691,7 +716,7 @@ range_of_builtin_call (range_query &query, irange &r, gcall *call) prec = TYPE_PRECISION (TREE_TYPE (arg)); mini = 0; maxi = prec; - query.range_of_expr (r, arg, call); + src.get_operand (r, arg); // If arg is non-zero, then ffs or popcount are non-zero. if (!range_includes_zero_p (&r)) mini = 1; @@ -735,7 +760,7 @@ range_of_builtin_call (range_query &query, irange &r, gcall *call) } } - query.range_of_expr (r, arg, call); + src.get_operand (r, arg); // From clz of minimum we can compute result maximum. if (r.constant_p () && !r.varying_p ()) { @@ -800,7 +825,7 @@ range_of_builtin_call (range_query &query, irange &r, gcall *call) mini = -2; } } - query.range_of_expr (r, arg, call); + src.get_operand (r, arg); if (!r.undefined_p ()) { if (r.lower_bound () != 0) @@ -838,13 +863,13 @@ range_of_builtin_call (range_query &query, irange &r, gcall *call) r.set (build_int_cst (type, 0), build_int_cst (type, prec - 1)); return true; case CFN_UBSAN_CHECK_ADD: - range_of_builtin_ubsan_call (query, r, call, PLUS_EXPR); + range_of_builtin_ubsan_call (r, call, PLUS_EXPR, src); return true; case CFN_UBSAN_CHECK_SUB: - range_of_builtin_ubsan_call (query, r, call, MINUS_EXPR); + range_of_builtin_ubsan_call (r, call, MINUS_EXPR, src); return true; case CFN_UBSAN_CHECK_MUL: - range_of_builtin_ubsan_call (query, r, call, MULT_EXPR); + range_of_builtin_ubsan_call (r, call, MULT_EXPR, src); return true; case CFN_GOACC_DIM_SIZE: @@ -894,17 +919,11 @@ range_of_builtin_call (range_query &query, irange &r, gcall *call) } -bool -gimple_ranger::range_of_builtin_call (irange &r, gcall *call) -{ - return ::range_of_builtin_call (*this, r, call); -} - // Calculate a range for COND_EXPR statement S and return it in R. // If a range cannot be calculated, return false. bool -gimple_ranger::range_of_cond_expr (irange &r, gassign *s) +fold_using_range::range_of_cond_expr (irange &r, gassign *s, fur_source &src) { int_range_max cond_range, range1, range2; tree cond = gimple_assign_rhs1 (s); @@ -917,9 +936,9 @@ gimple_ranger::range_of_cond_expr (irange &r, gassign *s) if (!irange::supports_type_p (TREE_TYPE (op1))) return false; - range_of_expr (cond_range, cond, s); - range_of_expr (range1, op1, s); - range_of_expr (range2, op2, s); + src.get_operand (cond_range, cond); + src.get_operand (range1, op1); + src.get_operand (range2, op2); // If the condition is known, choose the appropriate expression. if (cond_range.singleton_p ()) @@ -1047,6 +1066,16 @@ gimple_ranger::range_on_edge (irange &r, edge e, tree name) return true; } +// fold_range wrapper for range_of_stmt to use as an internal client. + +bool +gimple_ranger::fold_range_internal (irange &r, gimple *s, tree name) +{ + fold_using_range f; + fur_source src (this, &m_cache, NULL, s); + return f.fold_stmt (r, s, src, name); +} + // Calculate a range for statement S and return it in R. If NAME is // provided it represents the SSA_NAME on the LHS of the statement. // It is only required if there is more than one lhs/output. Check @@ -1063,7 +1092,7 @@ gimple_ranger::range_of_stmt (irange &r, gimple *s, tree name) // If no name, simply call the base routine. if (!name) - return calc_stmt (r, s, NULL_TREE); + return fold_range_internal (r, s, NULL_TREE); if (!gimple_range_ssa_p (name)) return false; @@ -1074,7 +1103,7 @@ gimple_ranger::range_of_stmt (irange &r, gimple *s, tree name) // Otherwise calculate a new value. int_range_max tmp; - calc_stmt (tmp, s, name); + fold_range_internal (tmp, s, name); // Combine the new value with the old value. This is required because // the way value propagation works, when the IL changes on the fly we @@ -1216,20 +1245,32 @@ gimple_ranger::dump (FILE *f) // If SCEV has any information about phi node NAME, return it as a range in R. void -gimple_ranger::range_of_ssa_name_with_loop_info (irange &r, tree name, - class loop *l, gphi *phi) +fold_using_range::range_of_ssa_name_with_loop_info (irange &r, tree name, + class loop *l, gphi *phi, + fur_source &src) { gcc_checking_assert (TREE_CODE (name) == SSA_NAME); tree min, max, type = TREE_TYPE (name); - if (bounds_of_var_in_loop (&min, &max, this, l, phi, name)) + if (bounds_of_var_in_loop (&min, &max, src.m_query, l, phi, name)) { - // ?? We could do better here. Since MIN/MAX can only be an - // SSA, SSA +- INTEGER_CST, or INTEGER_CST, we could easily call - // the ranger and solve anything not an integer. if (TREE_CODE (min) != INTEGER_CST) - min = vrp_val_min (type); + { + if (src.m_query + && src.m_query->range_of_expr (r, min, phi) + && !r.undefined_p ()) + min = wide_int_to_tree (type, r.lower_bound ()); + else + min = vrp_val_min (type); + } if (TREE_CODE (max) != INTEGER_CST) - max = vrp_val_max (type); + { + if (src.m_query + && src.m_query->range_of_expr (r, max, phi) + && !r.undefined_p ()) + max = wide_int_to_tree (type, r.upper_bound ()); + else + max = vrp_val_max (type); + } r.set (min, max); } else diff --git a/gcc/gimple-range.h b/gcc/gimple-range.h index f331561..5320506 100644 --- a/gcc/gimple-range.h +++ b/gcc/gimple-range.h @@ -30,6 +30,18 @@ along with GCC; see the file COPYING3. If not see #include "gimple-range-cache.h" #include "value-query.h" +// This file is the main include point for gimple ranges. +// There are two fold_range routines of interest: +// bool fold_range (irange &r, gimple *s, range_query *q) +// bool fold_range (irange &r, gimple *s, edge on_edge, range_query *q) +// These routines will fold stmt S into the result irange R. +// Any ssa_names on the stmt will be calculated using the range_query +// parameter via a call to range_of_expr. +// If no range_query is provided, current global range info will be used. +// The second variation specifies an edge, and stmt S is recalculated as if +// it appeared on that edge. + + // This is the basic range generator interface. // // This base class provides all the API entry points, but only provides @@ -55,32 +67,116 @@ public: void export_global_ranges (); void dump (FILE *f); protected: - bool calc_stmt (irange &r, gimple *s, tree name = NULL_TREE); - bool range_of_range_op (irange &r, gimple *s); - bool range_of_call (irange &r, gcall *call); - bool range_of_cond_expr (irange &r, gassign* cond); + bool fold_range_internal (irange &r, gimple *s, tree name); ranger_cache m_cache; -private: - bool range_of_phi (irange &r, gphi *phi); - bool range_of_address (irange &r, gimple *s); - bool range_of_builtin_call (irange &r, gcall *call); - bool range_with_loop_info (irange &r, tree name); - void range_of_ssa_name_with_loop_info (irange &, tree, class loop *, - gphi *); }; -// Calculate a basic range for a tree expression. +// Source of an operand for fold_using_range. +// It can specify a stmt or and edge, or thru an internal API which uses +// the ranger cache. +// Its primary function is to retreive an operand from the source via a +// call thru the range_query object. + +class fur_source +{ + friend class fold_using_range; +public: + inline fur_source (range_query *q, edge e); + inline fur_source (range_query *q, gimple *s); + inline fur_source (range_query *q, class ranger_cache *g, edge e, gimple *s); + bool get_operand (irange &r, tree expr); +protected: + ranger_cache *m_cache; + range_query *m_query; + edge m_edge; + gimple *m_stmt; +}; + + +// This class uses ranges to fold a gimple statement producinf a range for +// the LHS. The source of all operands is supplied via the fur_source class +// which provides a range_query as well as a source location and any other +// required information. + +class fold_using_range +{ +public: + bool fold_stmt (irange &r, gimple *s, class fur_source &src, + tree name = NULL_TREE); +protected: + bool range_of_range_op (irange &r, gimple *s, fur_source &src); + bool range_of_call (irange &r, gcall *call, fur_source &src); + bool range_of_cond_expr (irange &r, gassign* cond, fur_source &src); + bool range_of_address (irange &r, gimple *s, fur_source &src); + bool range_of_builtin_call (irange &r, gcall *call, fur_source &src); + void range_of_builtin_ubsan_call (irange &r, gcall *call, tree_code code, + fur_source &src); + bool range_of_phi (irange &r, gphi *phi, fur_source &src); + void range_of_ssa_name_with_loop_info (irange &, tree, class loop *, gphi *, + fur_source &src); +}; + + +// Create a source for a query on an edge. + +inline +fur_source::fur_source (range_query *q, edge e) +{ + m_query = q; + m_cache = NULL; + m_edge = e; + m_stmt = NULL; +} + +// Create a source for a query at a statement. + +inline +fur_source::fur_source (range_query *q, gimple *s) +{ + m_query = q; + m_cache = NULL; + m_edge = NULL; + m_stmt = s; +} + +// Create a source for Ranger. THis can recalculate from a different location +// and can also set the dependency information as appropriate when invoked. + +inline +fur_source::fur_source (range_query *q, ranger_cache *g, edge e, gimple *s) +{ + m_query = q; + m_cache = g; + m_edge = e; + m_stmt = s; +} + +// Fold stmt S into range R using range query Q. + +inline bool +fold_range (irange &r, gimple *s, range_query *q = NULL) +{ + fold_using_range f; + fur_source src (q, s); + return f.fold_stmt (r, s, src); +} + +// Recalculate stmt S into R using range query Q as if it were on edge ON_EDGE. + +inline bool +fold_range (irange &r, gimple *s, edge on_edge, range_query *q = NULL) +{ + fold_using_range f; + fur_source src (q, on_edge); + return f.fold_stmt (r, s, src); +} + +// Calculate a basic range for a tree node expression. extern bool get_tree_range (irange &r, tree expr); // These routines provide a GIMPLE interface to the range-ops code. extern tree gimple_range_operand1 (const gimple *s); extern tree gimple_range_operand2 (const gimple *s); -extern tree gimple_range_base_of_assignment (const gimple *s); -extern bool gimple_range_fold (irange &res, const gimple *s, - const irange &r1); -extern bool gimple_range_fold (irange &res, const gimple *s, - const irange &r1, - const irange &r2); extern bool gimple_range_calc_op1 (irange &r, const gimple *s, const irange &lhs_range); extern bool gimple_range_calc_op1 (irange &r, const gimple *s, @@ -199,7 +295,4 @@ private: // Flag to enable debugging the various internal Caches. #define DEBUG_RANGE_CACHE (dump_file && (param_evrp_mode & EVRP_MODE_DEBUG)) -// Temporary external interface to share with vr_values. -bool range_of_builtin_call (range_query &query, irange &r, gcall *call); - #endif // GCC_GIMPLE_RANGE_STMT_H diff --git a/gcc/vr-values.c b/gcc/vr-values.c index b1bf53a..02bc0db 100644 --- a/gcc/vr-values.c +++ b/gcc/vr-values.c @@ -1229,7 +1229,7 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt) return; break; default: - if (range_of_builtin_call (*this, *vr, as_a (stmt))) + if (fold_range (*vr, stmt, this)) { /* The original code nuked equivalences every time a range was found, so do the same here. */