aboutsummaryrefslogtreecommitdiff
path: root/gcc/gimple-range.cc
diff options
context:
space:
mode:
authorAndrew MacLeod <amacleod@redhat.com>2022-10-18 16:29:49 -0400
committerAndrew MacLeod <amacleod@redhat.com>2022-10-19 20:37:11 -0400
commit53e6d7a3102409f0f2a5a9ffbfbeaa62c135d991 (patch)
tree4a51214bd66f7d9ce476499a83cf4232b22feca4 /gcc/gimple-range.cc
parent87f9c4a433523a41bcb314bdce91b55a98d00e28 (diff)
downloadgcc-53e6d7a3102409f0f2a5a9ffbfbeaa62c135d991.zip
gcc-53e6d7a3102409f0f2a5a9ffbfbeaa62c135d991.tar.gz
gcc-53e6d7a3102409f0f2a5a9ffbfbeaa62c135d991.tar.bz2
Add assume support to VRP.
This provides an assume_query class using rangers GORI module to determine what ranges would be applied to any SSA NAMES in the function if the return value were [1, 1]. Any parameter ranges are stored in the SSA_NAME_RANGE_INFO field, and ranger's inferred range machinery is then used to look these up and match them to assume call parameteres in the bodies of other functions.. PR c++/106654 gcc/ * gimple-range-gori.h (compute_operand_range): Make public. * gimple-range-infer.cc (gimple_infer_range::check_assume_func): New. (gimple_infer_range::gimple_infer_range): Check for assume calls. * gimple-range-infer.h (check_assume_func): Add prototype. * gimple-range.cc (assume_query::assume_range_p): New. (assume_query::range_of_expr): New. (assume_query::assume_query): New. (assume_query::calculate_op): New. (assume_query::calculate_phi): New. (assume_query::check_taken_edge): New. (assume_query::calculate_stmt): New. (assume_query::dump): New. * gimple-range.h (class assume_query): New. * tree-vrp.cc (pass_assumptions::execute): Add processing. gcc/testsuite/ * g++.dg/cpp23/attr-assume-opt.C: New.
Diffstat (limited to 'gcc/gimple-range.cc')
-rw-r--r--gcc/gimple-range.cc190
1 files changed, 190 insertions, 0 deletions
diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc
index d67d649..0584397 100644
--- a/gcc/gimple-range.cc
+++ b/gcc/gimple-range.cc
@@ -645,3 +645,193 @@ disable_ranger (struct function *fun)
delete fun->x_range_query;
fun->x_range_query = NULL;
}
+
+// ------------------------------------------------------------------------
+
+// If there is a non-varying value associated with NAME, return true and the
+// range in R.
+
+bool
+assume_query::assume_range_p (vrange &r, tree name)
+{
+ if (global.get_global_range (r, name))
+ return !r.varying_p ();
+ return false;
+}
+
+// Query used by GORI to pick up any known value on entry to a block.
+
+bool
+assume_query::range_of_expr (vrange &r, tree expr, gimple *stmt)
+{
+ if (!gimple_range_ssa_p (expr))
+ return get_tree_range (r, expr, stmt);
+
+ if (!global.get_global_range (r, expr))
+ r.set_varying (TREE_TYPE (expr));
+ return true;
+}
+
+// If the current function returns an integral value, and has a single return
+// statement, it will calculate any SSA_NAMES is can determine ranges forr
+// assuming the function returns 1.
+
+assume_query::assume_query ()
+{
+ basic_block exit_bb = EXIT_BLOCK_PTR_FOR_FN (cfun);
+ if (single_pred_p (exit_bb))
+ {
+ basic_block bb = single_pred (exit_bb);
+ gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb);
+ if (gsi_end_p (gsi))
+ return;
+ gimple *s = gsi_stmt (gsi);
+ if (!is_a<greturn *> (s))
+ return;
+ greturn *gret = as_a<greturn *> (s);
+ tree op = gimple_return_retval (gret);
+ if (!gimple_range_ssa_p (op))
+ return;
+ tree lhs_type = TREE_TYPE (op);
+ if (!irange::supports_p (lhs_type))
+ return;
+
+ unsigned prec = TYPE_PRECISION (lhs_type);
+ int_range<2> lhs_range (lhs_type, wi::one (prec), wi::one (prec));
+ global.set_global_range (op, lhs_range);
+
+ gimple *def = SSA_NAME_DEF_STMT (op);
+ if (!def || gimple_get_lhs (def) != op)
+ return;
+ fur_stmt src (gret, this);
+ calculate_stmt (def, lhs_range, src);
+ }
+}
+
+// Evaluate operand OP on statement S, using the provided LHS range.
+// If successful, set the range in the global table, then visit OP's def stmt.
+
+void
+assume_query::calculate_op (tree op, gimple *s, vrange &lhs, fur_source &src)
+{
+ Value_Range op_range (TREE_TYPE (op));
+ if (m_gori.compute_operand_range (op_range, s, lhs, op, src)
+ && !op_range.varying_p ())
+ {
+ Value_Range range (TREE_TYPE (op));
+ if (global.get_global_range (range, op))
+ op_range.intersect (range);
+ global.set_global_range (op, op_range);
+ gimple *def_stmt = SSA_NAME_DEF_STMT (op);
+ if (def_stmt && gimple_get_lhs (def_stmt) == op)
+ calculate_stmt (def_stmt, op_range, src);
+ }
+}
+
+// Evaluate PHI statement, using the provided LHS range.
+// Check each constant argument predecessor if it can be taken
+// provide LHS to any symbolic argmeuents, and process their def statements.
+
+void
+assume_query::calculate_phi (gphi *phi, vrange &lhs_range, fur_source &src)
+{
+ for (unsigned x= 0; x < gimple_phi_num_args (phi); x++)
+ {
+ tree arg = gimple_phi_arg_def (phi, x);
+ Value_Range arg_range (TREE_TYPE (arg));
+ if (gimple_range_ssa_p (arg))
+ {
+ // A symbol arg will be the LHS value.
+ arg_range = lhs_range;
+ range_cast (arg_range, TREE_TYPE (arg));
+ if (!global.get_global_range (arg_range, arg))
+ {
+ global.set_global_range (arg, arg_range);
+ gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
+ if (def_stmt && gimple_get_lhs (def_stmt) == arg)
+ calculate_stmt (def_stmt, arg_range, src);
+ }
+ }
+ else if (get_tree_range (arg_range, arg, NULL))
+ {
+ // If this is a constant value that differs from LHS, this
+ // edge cannot be taken.
+ arg_range.intersect (lhs_range);
+ if (arg_range.undefined_p ())
+ continue;
+ // Otherwise check the condition feeding this edge.
+ edge e = gimple_phi_arg_edge (phi, x);
+ check_taken_edge (e, src);
+ }
+ }
+}
+
+// If an edge is known to be taken, examine the outgoing edge to see
+// if it carries any range information that can also be evaluated.
+
+void
+assume_query::check_taken_edge (edge e, fur_source &src)
+{
+ gimple *stmt = gimple_outgoing_range_stmt_p (e->src);
+ if (stmt && is_a<gcond *> (stmt))
+ {
+ int_range<2> cond;
+ gcond_edge_range (cond, e);
+ calculate_stmt (stmt, cond, src);
+ }
+}
+
+// Evaluate statement S which produces range LHS_RANGE.
+
+void
+assume_query::calculate_stmt (gimple *s, vrange &lhs_range, fur_source &src)
+{
+ gimple_range_op_handler handler (s);
+ if (handler)
+ {
+ tree op = gimple_range_ssa_p (handler.operand1 ());
+ if (op)
+ calculate_op (op, s, lhs_range, src);
+ op = gimple_range_ssa_p (handler.operand2 ());
+ if (op)
+ calculate_op (op, s, lhs_range, src);
+ }
+ else if (is_a<gphi *> (s))
+ {
+ calculate_phi (as_a<gphi *> (s), lhs_range, src);
+ // Don't further check predecessors of blocks with PHIs.
+ return;
+ }
+
+ // Even if the walk back terminates before the top, if this is a single
+ // predecessor block, see if the predecessor provided any ranges to get here.
+ if (single_pred_p (gimple_bb (s)))
+ check_taken_edge (single_pred_edge (gimple_bb (s)), src);
+}
+
+// Show everything that was calculated.
+
+void
+assume_query::dump (FILE *f)
+{
+ fprintf (f, "Assumption details calculated:\n");
+ for (unsigned i = 0; i < num_ssa_names; i++)
+ {
+ tree name = ssa_name (i);
+ if (!name || !gimple_range_ssa_p (name))
+ continue;
+ tree type = TREE_TYPE (name);
+ if (!Value_Range::supports_type_p (type))
+ continue;
+
+ Value_Range assume_range (type);
+ if (assume_range_p (assume_range, name))
+ {
+ print_generic_expr (f, name, TDF_SLIM);
+ fprintf (f, " -> ");
+ assume_range.dump (f);
+ fputc ('\n', f);
+ }
+ }
+ fprintf (f, "------------------------------\n");
+}