mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
Reimplement 'assume' processing pass.
Rework the assume pass to work properly and fail conservatively when it does. Also move it to its own file. PR tree-optimization/117287 gcc/ * Makefile.in (IBJS): Add tree-assume.o * gimple-range.cc (assume_query::assume_range_p): Remove. (assume_query::range_of_expr): Remove. (assume_query::assume_query): Move to tree-assume.cc. (assume_query::~assume_query): Remove. (assume_query::calculate_op): Move to tree-assume.cc. (assume_query::calculate_phi): Likewise. (assume_query::check_taken_edge): Remove. (assume_query::calculate_stmt): Move to tree-assume.cc. (assume_query::dump): Remove. * gimple-range.h (class assume_query): Move to tree-assume.cc * tree-assume.cc: New * tree-vrp.cc (struct pass_data_assumptions): Move to tree-assume.cc. (class pass_assumptions): Likewise. (make_pass_assumptions): Likewise. gcc/testsuite/ * g++.dg/cpp23/pr117287-attr.C: New.
This commit is contained in:
parent
fe22e18c1b
commit
62420dbd10
@ -1703,6 +1703,7 @@ OBJS = \
|
|||||||
sanopt.o \
|
sanopt.o \
|
||||||
sancov.o \
|
sancov.o \
|
||||||
simple-diagnostic-path.o \
|
simple-diagnostic-path.o \
|
||||||
|
tree-assume.o \
|
||||||
tree-call-cdce.o \
|
tree-call-cdce.o \
|
||||||
tree-cfg.o \
|
tree-cfg.o \
|
||||||
tree-cfgcleanup.o \
|
tree-cfgcleanup.o \
|
||||||
|
@ -700,200 +700,6 @@ disable_ranger (struct function *fun)
|
|||||||
fun->x_range_query = NULL;
|
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_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_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 it can determine ranges for
|
|
||||||
// assuming the function returns 1.
|
|
||||||
|
|
||||||
assume_query::assume_query ()
|
|
||||||
{
|
|
||||||
create_gori (0, param_vrp_switch_limit);
|
|
||||||
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_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assume_query::~assume_query ()
|
|
||||||
{
|
|
||||||
destroy_gori ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (gori ().compute_operand_range (op_range, s, lhs, op, src)
|
|
||||||
&& !op_range.varying_p ())
|
|
||||||
{
|
|
||||||
// Set the global range, merging if there is already a range.
|
|
||||||
global.merge_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 arguments, 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_range (arg_range, arg))
|
|
||||||
{
|
|
||||||
global.set_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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// The DOM based ranger assumes a single DOM walk through the IL, and is
|
// The DOM based ranger assumes a single DOM walk through the IL, and is
|
||||||
|
@ -82,23 +82,6 @@ extern gimple_ranger *enable_ranger (struct function *m,
|
|||||||
bool use_imm_uses = true);
|
bool use_imm_uses = true);
|
||||||
extern void disable_ranger (struct function *);
|
extern void disable_ranger (struct function *);
|
||||||
|
|
||||||
class assume_query : public range_query
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
assume_query ();
|
|
||||||
~assume_query ();
|
|
||||||
bool assume_range_p (vrange &r, tree name);
|
|
||||||
virtual bool range_of_expr (vrange &r, tree expr, gimple * = NULL);
|
|
||||||
void dump (FILE *f);
|
|
||||||
protected:
|
|
||||||
void calculate_stmt (gimple *s, vrange &lhs_range, fur_source &src);
|
|
||||||
void calculate_op (tree op, gimple *s, vrange &lhs, fur_source &src);
|
|
||||||
void calculate_phi (gphi *phi, vrange &lhs_range, fur_source &src);
|
|
||||||
void check_taken_edge (edge e, fur_source &src);
|
|
||||||
|
|
||||||
ssa_lazy_cache global;
|
|
||||||
};
|
|
||||||
|
|
||||||
// DOM based ranger for fast VRP.
|
// DOM based ranger for fast VRP.
|
||||||
// This must be processed in DOM order, and does only basic range operations.
|
// This must be processed in DOM order, and does only basic range operations.
|
||||||
|
|
||||||
|
38
gcc/testsuite/g++.dg/cpp23/pr117287-attr.C
Normal file
38
gcc/testsuite/g++.dg/cpp23/pr117287-attr.C
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// P1774R8 - Portable assumptions
|
||||||
|
// { dg-do run { target c++23 } }
|
||||||
|
// { dg-options "-O2 --param=logical-op-non-short-circuit=0" }
|
||||||
|
// Test the we can optimize based on conditions in assume.
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
foo (unsigned x)
|
||||||
|
{
|
||||||
|
return x == 4 || x == 5 || x == 9 || x == 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
int v;
|
||||||
|
|
||||||
|
[[gnu::noipa]] void
|
||||||
|
bar (const char *p)
|
||||||
|
{
|
||||||
|
if (p[0] != (v ? 'a' : 'b') || p[1])
|
||||||
|
__builtin_abort ();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[gnu::noipa]] void
|
||||||
|
baz (unsigned x)
|
||||||
|
{
|
||||||
|
bool a = x == 5;
|
||||||
|
[[assume (foo (x))]];
|
||||||
|
bar (a ? "a" : "b");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main ()
|
||||||
|
{
|
||||||
|
baz (4);
|
||||||
|
v = 1;
|
||||||
|
baz (5);
|
||||||
|
v = 0;
|
||||||
|
baz (9);
|
||||||
|
baz (10);
|
||||||
|
}
|
369
gcc/tree-assume.cc
Normal file
369
gcc/tree-assume.cc
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
/* Support for C++23 ASSUME keyword functionailty.
|
||||||
|
Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||||
|
Contributed by Andrew MacLeod <amacleod@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 "basic-block.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "function.h"
|
||||||
|
#include "cfg.h"
|
||||||
|
#include "tree.h"
|
||||||
|
#include "gimple.h"
|
||||||
|
#include "tree-pass.h"
|
||||||
|
#include "ssa.h"
|
||||||
|
#include "gimple-iterator.h"
|
||||||
|
#include "gimple-range.h"
|
||||||
|
#include "tree-dfa.h"
|
||||||
|
|
||||||
|
// An assume query utilizes the current range query to implelemtn the assume
|
||||||
|
// keyword.
|
||||||
|
// For any return value of 1 from the function, it attempts to determine
|
||||||
|
// which paths leads to a 1 value being returned. On those paths, what
|
||||||
|
// the ranges of any ssa_names listed in bitmap P (usually the parm list for
|
||||||
|
// the function) are, and combined them all.
|
||||||
|
// These ranges are then set as the global ranges for those parms in this
|
||||||
|
// function.
|
||||||
|
// Other functions which then refer to this function in an assume builtin
|
||||||
|
// will then pick up these ranges for the paramters via the inferred range
|
||||||
|
// mechanism.
|
||||||
|
// See gimple-range-infer.cc::gimple_infer_range::check_assume_func ()
|
||||||
|
//
|
||||||
|
// my_func (int x)
|
||||||
|
// {
|
||||||
|
// <...>
|
||||||
|
// assume [[(x == 1 || x ==4))]]
|
||||||
|
// if (x ==3)
|
||||||
|
//
|
||||||
|
// a small temporary assume function consisting of
|
||||||
|
// assume_f1 (int x) { return x == 1 || x == 4; }
|
||||||
|
// is constructed by the front end, and optimzed, at the very end of
|
||||||
|
// optimization, instead of generating code, we instead invoke the assume pass
|
||||||
|
// which uses this query to set the the global value of parm x to [1,1][4,4]
|
||||||
|
//
|
||||||
|
// Meanwhile., my_Fund has been rewritten to be:
|
||||||
|
//
|
||||||
|
// my_func (int x_2)
|
||||||
|
// {
|
||||||
|
// <...>
|
||||||
|
// assume_builtin_call assume_f1 (x_2);
|
||||||
|
// if (x_2 == 3)
|
||||||
|
//
|
||||||
|
// When ranger is processing the assume_builtin_call, it looks up the global
|
||||||
|
// value of the paramter in assume_f1, which is [1,1][4,4]. It then registers
|
||||||
|
// and inferred range at this statement setting the value x_2 to [1,1][4,4]
|
||||||
|
//
|
||||||
|
// Any uses of x_2 after this statement will now utilzie this inferred range.
|
||||||
|
//
|
||||||
|
// When VRP precoesses if (x_2 == 3), it picks up the inferred range, and
|
||||||
|
// determines that x_2 can never be 3, and will rewrite the branch to
|
||||||
|
// if (0 != 0)
|
||||||
|
|
||||||
|
class assume_query
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
assume_query (function *f, bitmap p);
|
||||||
|
protected:
|
||||||
|
inline void process_stmts (gimple *s, vrange &lhs_range)
|
||||||
|
{
|
||||||
|
fur_depend src (s, get_range_query (m_func));
|
||||||
|
calculate_stmt (s, lhs_range, src);
|
||||||
|
update_parms (src);
|
||||||
|
}
|
||||||
|
void update_parms (fur_source &src);
|
||||||
|
void calculate_stmt (gimple *s, vrange &lhs_range, fur_source &src);
|
||||||
|
void calculate_op (tree op, gimple *s, vrange &lhs, fur_source &src);
|
||||||
|
void calculate_phi (gphi *phi, vrange &lhs_range);
|
||||||
|
|
||||||
|
ssa_lazy_cache m_path; // Values found on path
|
||||||
|
ssa_lazy_cache m_parms; // Cumulative parameter value calculated
|
||||||
|
bitmap &m_parm_list; // Parameter ssa-names list.
|
||||||
|
function *m_func;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If function F returns a integral value, and has a single return
|
||||||
|
// statement, try to calculate the range of each value in P that leads
|
||||||
|
// to the return statement returning TRUE.
|
||||||
|
|
||||||
|
assume_query::assume_query (function *f, bitmap p) : m_parm_list (p),
|
||||||
|
m_func (f)
|
||||||
|
{
|
||||||
|
basic_block exit_bb = EXIT_BLOCK_PTR_FOR_FN (f);
|
||||||
|
// If there is more than one precessor to the exit block, bail.
|
||||||
|
if (!single_pred_p (exit_bb))
|
||||||
|
return;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Check if the single return value is a symbolic and supported type.
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Only values of interest are when the return value is 1. The defintion
|
||||||
|
// of the return value must be in the same block, or we have
|
||||||
|
// complicated flow control we don't understand, and just return.
|
||||||
|
unsigned prec = TYPE_PRECISION (lhs_type);
|
||||||
|
int_range<2> lhs_range (lhs_type, wi::one (prec), wi::one (prec));
|
||||||
|
|
||||||
|
gimple *def = SSA_NAME_DEF_STMT (op);
|
||||||
|
if (!def || gimple_get_lhs (def) != op || gimple_bb (def) != bb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Determine if this is a PHI or a linear sequence to deal with.
|
||||||
|
if (is_a<gphi *> (def))
|
||||||
|
calculate_phi (as_a<gphi *> (def), lhs_range);
|
||||||
|
else
|
||||||
|
process_stmts (def, lhs_range);
|
||||||
|
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "Assumptions :\n--------------\n");
|
||||||
|
|
||||||
|
// Now export any interesting values that were found.
|
||||||
|
bitmap_iterator bi;
|
||||||
|
unsigned x;
|
||||||
|
EXECUTE_IF_SET_IN_BITMAP (m_parm_list, 0, x, bi)
|
||||||
|
{
|
||||||
|
tree name = ssa_name (x);
|
||||||
|
tree type = TREE_TYPE (name);
|
||||||
|
value_range assume_range (type);
|
||||||
|
// Set the global range of NAME to anything calculated.
|
||||||
|
if (m_parms.get_range (assume_range, name) && !assume_range.varying_p ())
|
||||||
|
set_range_info (name, assume_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function Will update all the current value of interesting parameters.
|
||||||
|
// It tries, in order:
|
||||||
|
// a) a range found via path calculations.
|
||||||
|
// b) range of the parm at SRC point in the IL. (either edge or stmt)
|
||||||
|
// c) VARYING if those options fail.
|
||||||
|
// The value is then unioned with any existing value, allowing for the
|
||||||
|
// cumulation of all ranges leading to the return that return 1.
|
||||||
|
|
||||||
|
void
|
||||||
|
assume_query::update_parms (fur_source &src)
|
||||||
|
{
|
||||||
|
// Merge any parameter values.
|
||||||
|
bitmap_iterator bi;
|
||||||
|
unsigned x;
|
||||||
|
EXECUTE_IF_SET_IN_BITMAP (m_parm_list, 0, x, bi)
|
||||||
|
{
|
||||||
|
tree name = ssa_name (x);
|
||||||
|
tree type = TREE_TYPE (name);
|
||||||
|
|
||||||
|
// Find a valu efrom calculations.
|
||||||
|
value_range glob_range (type);
|
||||||
|
if (!m_path.get_range (glob_range, name)
|
||||||
|
&& !src.get_operand (glob_range, name))
|
||||||
|
glob_range.set_varying (type);
|
||||||
|
|
||||||
|
// Find any current value of parm, and combine them.
|
||||||
|
value_range parm_range (type);
|
||||||
|
if (m_parms.get_range (parm_range, name))
|
||||||
|
glob_range.union_ (parm_range);
|
||||||
|
|
||||||
|
// Set this new value.
|
||||||
|
m_parms.set_range (name, glob_range);
|
||||||
|
}
|
||||||
|
// Now reset the path values for the next path.
|
||||||
|
m_path.clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Evaluate PHI statement, using the provided LHS range.
|
||||||
|
// Only process edge that are both taken and returns the LHS of the PHI.
|
||||||
|
|
||||||
|
void
|
||||||
|
assume_query::calculate_phi (gphi *phi, vrange &lhs_range)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
edge e = gimple_phi_arg_edge (phi, x);
|
||||||
|
value_range edge_range (TREE_TYPE (arg));
|
||||||
|
// If we can't get an edge range, be conservative and assume the
|
||||||
|
// edge can be taken.
|
||||||
|
// NOte this can be either an ssa_name or a constant.
|
||||||
|
if (get_range_query (m_func)->range_on_edge (edge_range, e, arg))
|
||||||
|
{
|
||||||
|
if (gimple_range_ssa_p (arg))
|
||||||
|
{
|
||||||
|
arg_range = lhs_range;
|
||||||
|
range_cast (arg_range, TREE_TYPE (arg));
|
||||||
|
|
||||||
|
// An SSA_NAME arg will start with the LHS value.
|
||||||
|
// Check the range of ARG on the edge leading here. If that range
|
||||||
|
// cannot be any value from the LHS of the PHI, then this branch
|
||||||
|
// will not be taken to return the LHS value and can be ignored.
|
||||||
|
arg_range.intersect (edge_range);
|
||||||
|
if (arg_range.undefined_p ())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If the def is in the immediate preceeding block, process it
|
||||||
|
// with GORI to determine what values can produce this
|
||||||
|
// argument value. Otherwise there is more flow, so just query
|
||||||
|
// the edge for parm ranges and be conservative.
|
||||||
|
gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
|
||||||
|
if (def_stmt && gimple_get_lhs (def_stmt) == arg
|
||||||
|
&& gimple_bb (def_stmt) == e->src)
|
||||||
|
{
|
||||||
|
process_stmts (def_stmt, arg_range);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Fall through to process the edge.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If this is a constant value that differs from LHS, this
|
||||||
|
// edge cannot be taken and we can ignore it. Otherwise fall
|
||||||
|
// thorugh and process the edge.
|
||||||
|
edge_range.intersect (lhs_range);
|
||||||
|
if (edge_range.undefined_p ())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If this point is reached the edge needs processing.
|
||||||
|
fur_edge src (e, get_range_query (m_func));
|
||||||
|
update_parms (src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate operand OP on statement S, using the provided LHS range.
|
||||||
|
// If successful, set the range in path table, then visit OP's def stmt
|
||||||
|
// if it is in the same BB.
|
||||||
|
|
||||||
|
void
|
||||||
|
assume_query::calculate_op (tree op, gimple *s, vrange &lhs, fur_source &src)
|
||||||
|
{
|
||||||
|
basic_block bb = gimple_bb (s);
|
||||||
|
value_range op_range (TREE_TYPE (op));
|
||||||
|
if (src.gori () &&
|
||||||
|
src.gori ()->compute_operand_range (op_range, s, lhs, op, src)
|
||||||
|
&& !op_range.varying_p ())
|
||||||
|
{
|
||||||
|
// Set the global range, merging if there is already a range.
|
||||||
|
m_path.merge_range (op, op_range);
|
||||||
|
gimple *def_stmt = SSA_NAME_DEF_STMT (op);
|
||||||
|
// Terminate if the patway leads to a different block as we
|
||||||
|
// are not analyzing flow.
|
||||||
|
if (def_stmt && gimple_get_lhs (def_stmt) == op
|
||||||
|
&& gimple_bb (def_stmt) == bb)
|
||||||
|
calculate_stmt (def_stmt, op_range, src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Evaluate statement S which produces range LHS_RANGE. Use GORI to
|
||||||
|
// determine what values the operands can have to produce the LHS,
|
||||||
|
// and set these in the M_PATH table.
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const pass_data pass_data_assumptions =
|
||||||
|
{
|
||||||
|
GIMPLE_PASS, /* type */
|
||||||
|
"assumptions", /* name */
|
||||||
|
OPTGROUP_NONE, /* optinfo_flags */
|
||||||
|
TV_TREE_ASSUMPTIONS, /* tv_id */
|
||||||
|
PROP_ssa, /* properties_required */
|
||||||
|
PROP_assumptions_done, /* properties_provided */
|
||||||
|
0, /* properties_destroyed */
|
||||||
|
0, /* todo_flags_start */
|
||||||
|
0, /* todo_flags_end */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class pass_assumptions : public gimple_opt_pass
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
pass_assumptions (gcc::context *ctxt)
|
||||||
|
: gimple_opt_pass (pass_data_assumptions, ctxt)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/* opt_pass methods: */
|
||||||
|
bool gate (function *fun) final override { return fun->assume_function; }
|
||||||
|
unsigned int execute (function *fun) final override
|
||||||
|
{
|
||||||
|
// Create a bitmap of all the paramters in this function.
|
||||||
|
// Invoke the assume_query to detemine what values these parameters
|
||||||
|
// have when the function returns TRUE, and set the globals value of
|
||||||
|
// those parameters in this function based on that. This will later be
|
||||||
|
// utilized by ranger when prcessing the builtin_assumer function.
|
||||||
|
auto_bitmap decls;
|
||||||
|
for (tree arg = DECL_ARGUMENTS (fun->decl); arg; arg = DECL_CHAIN (arg))
|
||||||
|
{
|
||||||
|
tree name = ssa_default_def (fun, arg);
|
||||||
|
if (!name || !gimple_range_ssa_p (name))
|
||||||
|
continue;
|
||||||
|
tree type = TREE_TYPE (name);
|
||||||
|
if (!value_range::supports_type_p (type))
|
||||||
|
continue;
|
||||||
|
bitmap_set_bit (decls, SSA_NAME_VERSION (name));
|
||||||
|
}
|
||||||
|
// If there are no parameters to map, simply return;
|
||||||
|
if (bitmap_empty_p (decls))
|
||||||
|
return TODO_discard_function;
|
||||||
|
|
||||||
|
enable_ranger (fun);
|
||||||
|
|
||||||
|
// This assume query will set any global values required.
|
||||||
|
assume_query query (fun, decls);
|
||||||
|
|
||||||
|
disable_ranger (fun);
|
||||||
|
return TODO_discard_function;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class pass_assumptions
|
||||||
|
|
||||||
|
} // anon namespace
|
||||||
|
|
||||||
|
gimple_opt_pass *
|
||||||
|
make_pass_assumptions (gcc::context *ctx)
|
||||||
|
{
|
||||||
|
return new pass_assumptions (ctx);
|
||||||
|
}
|
@ -1353,60 +1353,6 @@ public:
|
|||||||
const pass_data &data;
|
const pass_data &data;
|
||||||
bool final_p;
|
bool final_p;
|
||||||
}; // class pass_vrp
|
}; // class pass_vrp
|
||||||
|
|
||||||
const pass_data pass_data_assumptions =
|
|
||||||
{
|
|
||||||
GIMPLE_PASS, /* type */
|
|
||||||
"assumptions", /* name */
|
|
||||||
OPTGROUP_NONE, /* optinfo_flags */
|
|
||||||
TV_TREE_ASSUMPTIONS, /* tv_id */
|
|
||||||
PROP_ssa, /* properties_required */
|
|
||||||
PROP_assumptions_done, /* properties_provided */
|
|
||||||
0, /* properties_destroyed */
|
|
||||||
0, /* todo_flags_start */
|
|
||||||
0, /* todo_flags_end */
|
|
||||||
};
|
|
||||||
|
|
||||||
class pass_assumptions : public gimple_opt_pass
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
pass_assumptions (gcc::context *ctxt)
|
|
||||||
: gimple_opt_pass (pass_data_assumptions, ctxt)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/* opt_pass methods: */
|
|
||||||
bool gate (function *fun) final override { return fun->assume_function; }
|
|
||||||
unsigned int execute (function *) final override
|
|
||||||
{
|
|
||||||
assume_query query;
|
|
||||||
if (dump_file)
|
|
||||||
fprintf (dump_file, "Assumptions :\n--------------\n");
|
|
||||||
|
|
||||||
for (tree arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg))
|
|
||||||
{
|
|
||||||
tree name = ssa_default_def (cfun, arg);
|
|
||||||
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);
|
|
||||||
// Set the global range of NAME to anything calculated.
|
|
||||||
if (query.assume_range_p (assume_range, name))
|
|
||||||
set_range_info (name, assume_range);
|
|
||||||
}
|
|
||||||
if (dump_file)
|
|
||||||
{
|
|
||||||
fputc ('\n', dump_file);
|
|
||||||
gimple_dump_cfg (dump_file, dump_flags & ~TDF_DETAILS);
|
|
||||||
if (dump_flags & TDF_DETAILS)
|
|
||||||
query.dump (dump_file);
|
|
||||||
}
|
|
||||||
return TODO_discard_function;
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // class pass_assumptions
|
|
||||||
|
|
||||||
} // anon namespace
|
} // anon namespace
|
||||||
|
|
||||||
gimple_opt_pass *
|
gimple_opt_pass *
|
||||||
@ -1426,9 +1372,3 @@ make_pass_fast_vrp (gcc::context *ctxt)
|
|||||||
{
|
{
|
||||||
return new pass_vrp (ctxt, pass_data_fast_vrp);
|
return new pass_vrp (ctxt, pass_data_fast_vrp);
|
||||||
}
|
}
|
||||||
|
|
||||||
gimple_opt_pass *
|
|
||||||
make_pass_assumptions (gcc::context *ctx)
|
|
||||||
{
|
|
||||||
return new pass_assumptions (ctx);
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user