mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-21 19:35:36 -05:00
1924 lines
56 KiB
C++
1924 lines
56 KiB
C++
/* Support routines for Value Range Propagation (VRP).
|
|
Copyright (C) 2005-2026 Free Software Foundation, Inc.
|
|
|
|
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 "backend.h"
|
|
#include "insn-codes.h"
|
|
#include "tree.h"
|
|
#include "gimple.h"
|
|
#include "ssa.h"
|
|
#include "optabs-tree.h"
|
|
#include "gimple-pretty-print.h"
|
|
#include "diagnostic-core.h"
|
|
#include "flags.h"
|
|
#include "fold-const.h"
|
|
#include "calls.h"
|
|
#include "cfganal.h"
|
|
#include "gimple-iterator.h"
|
|
#include "gimple-fold.h"
|
|
#include "tree-cfg.h"
|
|
#include "tree-ssa-loop-niter.h"
|
|
#include "tree-ssa-loop.h"
|
|
#include "intl.h"
|
|
#include "cfgloop.h"
|
|
#include "tree-scalar-evolution.h"
|
|
#include "tree-ssa-propagate.h"
|
|
#include "tree-chrec.h"
|
|
#include "omp-general.h"
|
|
#include "case-cfn-macros.h"
|
|
#include "alloc-pool.h"
|
|
#include "attribs.h"
|
|
#include "range.h"
|
|
#include "vr-values.h"
|
|
#include "cfghooks.h"
|
|
#include "range-op.h"
|
|
#include "gimple-range.h"
|
|
|
|
/* Return true if op is in a boolean [0, 1] value-range. */
|
|
|
|
bool
|
|
simplify_using_ranges::op_with_boolean_value_range_p (tree op, gimple *s)
|
|
{
|
|
if (TYPE_PRECISION (TREE_TYPE (op)) == 1)
|
|
return true;
|
|
|
|
if (integer_zerop (op)
|
|
|| integer_onep (op))
|
|
return true;
|
|
|
|
if (TREE_CODE (op) != SSA_NAME)
|
|
return false;
|
|
|
|
/* ?? Errr, this should probably check for [0,0] and [1,1] as well
|
|
as [0,1]. */
|
|
int_range_max vr;
|
|
return (query->range_of_expr (vr, op, s)
|
|
&& vr == range_true_and_false (TREE_TYPE (op)));
|
|
}
|
|
|
|
/* Helper function for simplify_internal_call_using_ranges and
|
|
extract_range_basic. Return true if OP0 SUBCODE OP1 for
|
|
SUBCODE {PLUS,MINUS,MULT}_EXPR is known to never overflow or
|
|
always overflow. Set *OVF to true if it is known to always
|
|
overflow. */
|
|
|
|
static bool
|
|
check_for_binary_op_overflow (range_query *query,
|
|
enum tree_code subcode, tree type,
|
|
tree op0, tree op1, bool *ovf, gimple *s = NULL)
|
|
{
|
|
relation_kind rel = VREL_VARYING;
|
|
/* For subtraction see if relations could simplify it. */
|
|
if (s
|
|
&& subcode == MINUS_EXPR
|
|
&& types_compatible_p (TREE_TYPE (op0), TREE_TYPE (op1)))
|
|
{
|
|
rel = query->relation().query (s, op0, op1);
|
|
/* The result of the infinite precision subtraction of
|
|
the same values will be always 0. That will fit into any result
|
|
type. */
|
|
if (rel == VREL_EQ)
|
|
return true;
|
|
}
|
|
|
|
int_range_max vr0, vr1;
|
|
if (!query->range_of_expr (vr0, op0, s) || vr0.undefined_p ())
|
|
vr0.set_varying (TREE_TYPE (op0));
|
|
if (!query->range_of_expr (vr1, op1, s) || vr1.undefined_p ())
|
|
vr1.set_varying (TREE_TYPE (op1));
|
|
|
|
tree vr0min = wide_int_to_tree (TREE_TYPE (op0), vr0.lower_bound ());
|
|
tree vr0max = wide_int_to_tree (TREE_TYPE (op0), vr0.upper_bound ());
|
|
tree vr1min = wide_int_to_tree (TREE_TYPE (op1), vr1.lower_bound ());
|
|
tree vr1max = wide_int_to_tree (TREE_TYPE (op1), vr1.upper_bound ());
|
|
|
|
/* If op1 is not negative, op0 - op1 in infinite precision for op0 >= op1
|
|
will be always in [0, op0] and so if vr0max - vr1min fits into type,
|
|
there won't be any overflow. */
|
|
if ((rel == VREL_GT || rel == VREL_GE)
|
|
&& tree_int_cst_sgn (vr1min) >= 0
|
|
&& !arith_overflowed_p (MINUS_EXPR, type, vr0max, vr1min))
|
|
return true;
|
|
|
|
/* If op1 is not negative, op0 - op1 in infinite precision for op0 < op1
|
|
will be always in [-inf, -1] and so will always overflow if type is
|
|
unsigned. */
|
|
if (rel == VREL_LT
|
|
&& tree_int_cst_sgn (vr1min) >= 0
|
|
&& TYPE_UNSIGNED (type))
|
|
{
|
|
*ovf = true;
|
|
return true;
|
|
}
|
|
|
|
*ovf = arith_overflowed_p (subcode, type, vr0min,
|
|
subcode == MINUS_EXPR ? vr1max : vr1min);
|
|
if (arith_overflowed_p (subcode, type, vr0max,
|
|
subcode == MINUS_EXPR ? vr1min : vr1max) != *ovf)
|
|
return false;
|
|
if (subcode == MULT_EXPR)
|
|
{
|
|
if (arith_overflowed_p (subcode, type, vr0min, vr1max) != *ovf
|
|
|| arith_overflowed_p (subcode, type, vr0max, vr1min) != *ovf)
|
|
return false;
|
|
}
|
|
if (*ovf)
|
|
{
|
|
/* So far we found that there is an overflow on the boundaries.
|
|
That doesn't prove that there is an overflow even for all values
|
|
in between the boundaries. For that compute widest2_int range
|
|
of the result and see if it doesn't overlap the range of
|
|
type. */
|
|
widest2_int wmin, wmax;
|
|
widest2_int w[4];
|
|
int i;
|
|
signop sign0 = TYPE_SIGN (TREE_TYPE (op0));
|
|
signop sign1 = TYPE_SIGN (TREE_TYPE (op1));
|
|
w[0] = widest2_int::from (vr0.lower_bound (), sign0);
|
|
w[1] = widest2_int::from (vr0.upper_bound (), sign0);
|
|
w[2] = widest2_int::from (vr1.lower_bound (), sign1);
|
|
w[3] = widest2_int::from (vr1.upper_bound (), sign1);
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
widest2_int wt;
|
|
switch (subcode)
|
|
{
|
|
case PLUS_EXPR:
|
|
wt = wi::add (w[i & 1], w[2 + (i & 2) / 2]);
|
|
break;
|
|
case MINUS_EXPR:
|
|
wt = wi::sub (w[i & 1], w[2 + (i & 2) / 2]);
|
|
break;
|
|
case MULT_EXPR:
|
|
wt = wi::mul (w[i & 1], w[2 + (i & 2) / 2]);
|
|
break;
|
|
default:
|
|
gcc_unreachable ();
|
|
}
|
|
if (i == 0)
|
|
{
|
|
wmin = wt;
|
|
wmax = wt;
|
|
}
|
|
else
|
|
{
|
|
wmin = wi::smin (wmin, wt);
|
|
wmax = wi::smax (wmax, wt);
|
|
}
|
|
}
|
|
/* The result of op0 CODE op1 is known to be in range
|
|
[wmin, wmax]. */
|
|
widest2_int wtmin
|
|
= widest2_int::from (irange_val_min (type), TYPE_SIGN (type));
|
|
widest2_int wtmax
|
|
= widest2_int::from (irange_val_max (type), TYPE_SIGN (type));
|
|
/* If all values in [wmin, wmax] are smaller than
|
|
[wtmin, wtmax] or all are larger than [wtmin, wtmax],
|
|
the arithmetic operation will always overflow. */
|
|
if (wmax < wtmin || wmin > wtmax)
|
|
return true;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Set INIT, STEP, and DIRECTION to the corresponding values of NAME
|
|
within LOOP, and return TRUE. Otherwise return FALSE, and set R to
|
|
the conservative range of NAME within the loop. */
|
|
|
|
static bool
|
|
get_scev_info (vrange &r, tree name, gimple *stmt, class loop *l,
|
|
tree &init, tree &step, enum ev_direction &dir)
|
|
{
|
|
tree ev = analyze_scalar_evolution (l, name);
|
|
tree chrec = instantiate_parameters (l, ev);
|
|
tree type = TREE_TYPE (name);
|
|
if (TREE_CODE (chrec) != POLYNOMIAL_CHREC)
|
|
{
|
|
r.set_varying (type);
|
|
return false;
|
|
}
|
|
if (is_gimple_min_invariant (chrec))
|
|
{
|
|
if (is_gimple_constant (chrec))
|
|
r.set (chrec, chrec);
|
|
else
|
|
r.set_varying (type);
|
|
return false;
|
|
}
|
|
|
|
init = initial_condition_in_loop_num (chrec, l->num);
|
|
step = evolution_part_in_loop_num (chrec, l->num);
|
|
if (!init || !step)
|
|
{
|
|
r.set_varying (type);
|
|
return false;
|
|
}
|
|
dir = scev_direction (chrec);
|
|
if (dir == EV_DIR_UNKNOWN
|
|
|| scev_probably_wraps_p (NULL, init, step, stmt,
|
|
get_chrec_loop (chrec), true))
|
|
{
|
|
r.set_varying (type);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Return TRUE if STEP * NIT may overflow when calculated in TYPE. */
|
|
|
|
static bool
|
|
induction_variable_may_overflow_p (tree type,
|
|
const wide_int &step, const widest_int &nit)
|
|
{
|
|
wi::overflow_type ovf;
|
|
signop sign = TYPE_SIGN (type);
|
|
widest_int max_step = wi::mul (widest_int::from (step, sign),
|
|
nit, sign, &ovf);
|
|
|
|
if (ovf || !wi::fits_to_tree_p (max_step, type))
|
|
return true;
|
|
|
|
/* For a signed type we have to check whether the result has the
|
|
expected signedness which is that of the step as number of
|
|
iterations is unsigned. */
|
|
return (sign == SIGNED
|
|
&& wi::gts_p (max_step, 0) != wi::gts_p (step, 0));
|
|
}
|
|
|
|
/* Set R to the range from BEGIN to END, assuming the direction of the
|
|
loop is DIR. */
|
|
|
|
static void
|
|
range_from_loop_direction (irange &r, tree type,
|
|
const irange &begin, const irange &end,
|
|
ev_direction dir)
|
|
{
|
|
signop sign = TYPE_SIGN (type);
|
|
|
|
if (begin.undefined_p () || end.undefined_p ())
|
|
r.set_varying (type);
|
|
else if (dir == EV_DIR_GROWS)
|
|
{
|
|
if (wi::ge_p (begin.lower_bound (), end.upper_bound (), sign))
|
|
r.set_varying (type);
|
|
else
|
|
r = int_range<1> (type, begin.lower_bound (), end.upper_bound ());
|
|
}
|
|
else
|
|
{
|
|
if (wi::ge_p (end.lower_bound (), begin.upper_bound (), sign))
|
|
r.set_varying (type);
|
|
else
|
|
r = int_range<1> (type, end.lower_bound (), begin.upper_bound ());
|
|
}
|
|
}
|
|
|
|
/* Set V to the range of NAME in STMT within LOOP. Return TRUE if a
|
|
range was found. */
|
|
|
|
bool
|
|
range_of_var_in_loop (vrange &v, tree name, class loop *l, gimple *stmt,
|
|
range_query *query)
|
|
{
|
|
tree init, step;
|
|
enum ev_direction dir;
|
|
if (!get_scev_info (v, name, stmt, l, init, step, dir))
|
|
return true;
|
|
|
|
// Calculate ranges for the values from SCEV.
|
|
irange &r = as_a <irange> (v);
|
|
tree type = TREE_TYPE (init);
|
|
int_range<2> rinit (type), rstep (type), max_init (type);
|
|
if (!query->range_of_expr (rinit, init, stmt)
|
|
|| !query->range_of_expr (rstep, step, stmt))
|
|
return false;
|
|
|
|
// Calculate the final range of NAME if possible.
|
|
if (rinit.singleton_p () && rstep.singleton_p ())
|
|
{
|
|
widest_int nit;
|
|
if (!max_loop_iterations (l, &nit))
|
|
return false;
|
|
|
|
if (!induction_variable_may_overflow_p (type, rstep.lower_bound (), nit))
|
|
{
|
|
// Calculate the max bounds for init (init + niter * step).
|
|
wide_int w = wide_int::from (nit, TYPE_PRECISION (type), TYPE_SIGN (type));
|
|
int_range<1> niter (type, w, w);
|
|
int_range_max max_step;
|
|
range_op_handler mult_handler (MULT_EXPR);
|
|
range_op_handler plus_handler (PLUS_EXPR);
|
|
if (!mult_handler.fold_range (max_step, type, niter, rstep)
|
|
|| !plus_handler.fold_range (max_init, type, rinit, max_step))
|
|
return false;
|
|
}
|
|
}
|
|
range_from_loop_direction (r, type, rinit, max_init, dir);
|
|
return true;
|
|
}
|
|
|
|
/* Helper function for vrp_evaluate_conditional_warnv & other
|
|
optimizers. */
|
|
|
|
tree
|
|
simplify_using_ranges::fold_cond_with_ops (enum tree_code code,
|
|
tree op0, tree op1, gimple *s)
|
|
{
|
|
value_range r0 (TREE_TYPE (op0));
|
|
value_range r1 (TREE_TYPE (op1));
|
|
if (!query->range_of_expr (r0, op0, s)
|
|
|| !query->range_of_expr (r1, op1, s))
|
|
return NULL_TREE;
|
|
|
|
int_range<1> res;
|
|
range_op_handler handler (code);
|
|
|
|
// Find any relation between op0 and op1 and pass it to fold_range.
|
|
relation_kind rel = VREL_VARYING;
|
|
if (gimple_range_ssa_p (op0) && gimple_range_ssa_p (op1))
|
|
rel = query->relation ().query (s, op0, op1);
|
|
|
|
if (handler && handler.fold_range (res, boolean_type_node, r0, r1,
|
|
relation_trio::op1_op2 (rel)))
|
|
{
|
|
if (res == range_true ())
|
|
return boolean_true_node;
|
|
if (res == range_false ())
|
|
return boolean_false_node;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Helper function for legacy_fold_cond. */
|
|
|
|
tree
|
|
simplify_using_ranges::legacy_fold_cond_overflow (gimple *stmt)
|
|
{
|
|
tree ret;
|
|
tree_code code = gimple_cond_code (stmt);
|
|
tree op0 = gimple_cond_lhs (stmt);
|
|
tree op1 = gimple_cond_rhs (stmt);
|
|
|
|
/* We only deal with integral and pointer types. */
|
|
if (!INTEGRAL_TYPE_P (TREE_TYPE (op0))
|
|
&& !POINTER_TYPE_P (TREE_TYPE (op0)))
|
|
return NULL_TREE;
|
|
|
|
/* If OP0 CODE OP1 is an overflow comparison, if it can be expressed
|
|
as a simple equality test, then prefer that over its current form
|
|
for evaluation.
|
|
|
|
An overflow test which collapses to an equality test can always be
|
|
expressed as a comparison of one argument against zero. Overflow
|
|
occurs when the chosen argument is zero and does not occur if the
|
|
chosen argument is not zero. */
|
|
tree x;
|
|
if (overflow_comparison_p (code, op0, op1, &x))
|
|
{
|
|
wide_int max = wi::max_value (TYPE_PRECISION (TREE_TYPE (op0)), UNSIGNED);
|
|
/* B = A - 1; if (A < B) -> B = A - 1; if (A == 0)
|
|
B = A - 1; if (A > B) -> B = A - 1; if (A != 0)
|
|
B = A + 1; if (B < A) -> B = A + 1; if (B == 0)
|
|
B = A + 1; if (B > A) -> B = A + 1; if (B != 0) */
|
|
if (integer_zerop (x))
|
|
{
|
|
op1 = x;
|
|
code = (code == LT_EXPR || code == LE_EXPR) ? EQ_EXPR : NE_EXPR;
|
|
}
|
|
/* B = A + 1; if (A > B) -> B = A + 1; if (B == 0)
|
|
B = A + 1; if (A < B) -> B = A + 1; if (B != 0)
|
|
B = A - 1; if (B > A) -> B = A - 1; if (A == 0)
|
|
B = A - 1; if (B < A) -> B = A - 1; if (A != 0) */
|
|
else if (wi::to_wide (x) == max - 1)
|
|
{
|
|
op0 = op1;
|
|
op1 = wide_int_to_tree (TREE_TYPE (op0), 0);
|
|
code = (code == GT_EXPR || code == GE_EXPR) ? EQ_EXPR : NE_EXPR;
|
|
}
|
|
else
|
|
{
|
|
int_range_max vro, vri;
|
|
tree type = TREE_TYPE (op0);
|
|
if (code == GT_EXPR || code == GE_EXPR)
|
|
{
|
|
vro.set (type,
|
|
wi::to_wide (TYPE_MIN_VALUE (type)),
|
|
wi::to_wide (x), VR_ANTI_RANGE);
|
|
vri.set (type,
|
|
wi::to_wide (TYPE_MIN_VALUE (type)),
|
|
wi::to_wide (x));
|
|
}
|
|
else if (code == LT_EXPR || code == LE_EXPR)
|
|
{
|
|
vro.set (type,
|
|
wi::to_wide (TYPE_MIN_VALUE (type)),
|
|
wi::to_wide (x));
|
|
vri.set (type,
|
|
wi::to_wide (TYPE_MIN_VALUE (type)),
|
|
wi::to_wide (x),
|
|
VR_ANTI_RANGE);
|
|
}
|
|
else
|
|
gcc_unreachable ();
|
|
int_range_max vr0;
|
|
if (!query->range_of_expr (vr0, op0, stmt))
|
|
vr0.set_varying (TREE_TYPE (op0));
|
|
/* If vro, the range for OP0 to pass the overflow test, has
|
|
no intersection with *vr0, OP0's known range, then the
|
|
overflow test can't pass, so return the node for false.
|
|
If it is the inverted range, vri, that has no
|
|
intersection, then the overflow test must pass, so return
|
|
the node for true. In other cases, we could proceed with
|
|
a simplified condition comparing OP0 and X, with LE_EXPR
|
|
for previously LE_ or LT_EXPR and GT_EXPR otherwise, but
|
|
the comments next to the enclosing if suggest it's not
|
|
generally profitable to do so. */
|
|
vro.intersect (vr0);
|
|
if (vro.undefined_p ())
|
|
return boolean_false_node;
|
|
vri.intersect (vr0);
|
|
if (vri.undefined_p ())
|
|
return boolean_true_node;
|
|
}
|
|
}
|
|
|
|
if ((ret = fold_cond_with_ops (code, op0, op1, stmt)))
|
|
return ret;
|
|
return NULL_TREE;
|
|
}
|
|
|
|
/* Visit conditional statement STMT. If we can determine which edge
|
|
will be taken out of STMT's basic block, record it in
|
|
*TAKEN_EDGE_P. Otherwise, set *TAKEN_EDGE_P to NULL. */
|
|
|
|
void
|
|
simplify_using_ranges::legacy_fold_cond (gcond *stmt, edge *taken_edge_p)
|
|
{
|
|
tree val;
|
|
|
|
*taken_edge_p = NULL;
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
tree use;
|
|
ssa_op_iter i;
|
|
|
|
fprintf (dump_file, "\nVisiting conditional with predicate: ");
|
|
print_gimple_stmt (dump_file, stmt, 0);
|
|
fprintf (dump_file, "\nWith known ranges\n");
|
|
|
|
FOR_EACH_SSA_TREE_OPERAND (use, stmt, i, SSA_OP_USE)
|
|
{
|
|
fprintf (dump_file, "\t");
|
|
print_generic_expr (dump_file, use);
|
|
fprintf (dump_file, ": ");
|
|
value_range r (TREE_TYPE (use));
|
|
query->range_of_expr (r, use, stmt);
|
|
r.dump (dump_file);
|
|
}
|
|
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
|
|
val = legacy_fold_cond_overflow (stmt);
|
|
if (val)
|
|
*taken_edge_p = find_taken_edge (gimple_bb (stmt), val);
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "\nPredicate evaluates to: ");
|
|
if (val == NULL_TREE)
|
|
fprintf (dump_file, "DON'T KNOW\n");
|
|
else
|
|
print_generic_stmt (dump_file, val);
|
|
}
|
|
}
|
|
|
|
/* Simplify boolean operations if the source is known
|
|
to be already a boolean. */
|
|
bool
|
|
simplify_using_ranges::simplify_truth_ops_using_ranges
|
|
(gimple_stmt_iterator *gsi,
|
|
gimple *stmt)
|
|
{
|
|
enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
|
|
tree lhs, op0, op1;
|
|
bool need_conversion;
|
|
|
|
/* We handle only !=/== case here. */
|
|
gcc_assert (rhs_code == EQ_EXPR || rhs_code == NE_EXPR);
|
|
|
|
op0 = gimple_assign_rhs1 (stmt);
|
|
if (!op_with_boolean_value_range_p (op0, stmt))
|
|
return false;
|
|
|
|
op1 = gimple_assign_rhs2 (stmt);
|
|
if (!op_with_boolean_value_range_p (op1, stmt))
|
|
return false;
|
|
|
|
/* Reduce number of cases to handle to NE_EXPR. As there is no
|
|
BIT_XNOR_EXPR we cannot replace A == B with a single statement. */
|
|
if (rhs_code == EQ_EXPR)
|
|
{
|
|
if (TREE_CODE (op1) == INTEGER_CST)
|
|
op1 = int_const_binop (BIT_XOR_EXPR, op1,
|
|
build_int_cst (TREE_TYPE (op1), 1));
|
|
else
|
|
return false;
|
|
}
|
|
|
|
lhs = gimple_assign_lhs (stmt);
|
|
need_conversion
|
|
= !useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (op0));
|
|
|
|
/* Make sure to not sign-extend a 1-bit 1 when converting the result. */
|
|
if (need_conversion
|
|
&& !TYPE_UNSIGNED (TREE_TYPE (op0))
|
|
&& TYPE_PRECISION (TREE_TYPE (op0)) == 1
|
|
&& TYPE_PRECISION (TREE_TYPE (lhs)) > 1)
|
|
return false;
|
|
|
|
/* For A != 0 we can substitute A itself. */
|
|
if (integer_zerop (op1))
|
|
gimple_assign_set_rhs_with_ops (gsi,
|
|
need_conversion
|
|
? NOP_EXPR : TREE_CODE (op0), op0);
|
|
/* For A != B we substitute A ^ B. Either with conversion. */
|
|
else if (need_conversion)
|
|
{
|
|
tree tem = make_ssa_name (TREE_TYPE (op0));
|
|
gassign *newop
|
|
= gimple_build_assign (tem, BIT_XOR_EXPR, op0, op1);
|
|
gsi_insert_before (gsi, newop, GSI_SAME_STMT);
|
|
if (INTEGRAL_TYPE_P (TREE_TYPE (tem))
|
|
&& TYPE_PRECISION (TREE_TYPE (tem)) > 1)
|
|
{
|
|
int_range<1> vr (TREE_TYPE (tem),
|
|
wi::zero (TYPE_PRECISION (TREE_TYPE (tem))),
|
|
wi::one (TYPE_PRECISION (TREE_TYPE (tem))));
|
|
set_range_info (tem, vr);
|
|
}
|
|
gimple_assign_set_rhs_with_ops (gsi, NOP_EXPR, tem);
|
|
}
|
|
/* Or without. */
|
|
else
|
|
gimple_assign_set_rhs_with_ops (gsi, BIT_XOR_EXPR, op0, op1);
|
|
update_stmt (gsi_stmt (*gsi));
|
|
fold_stmt (gsi, follow_single_use_edges);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Simplify a division or modulo operator to a right shift or bitwise and
|
|
if the first operand is unsigned or is greater than zero and the second
|
|
operand is an exact power of two. For TRUNC_MOD_EXPR op0 % op1 with
|
|
constant op1 (op1min = op1) or with op1 in [op1min, op1max] range,
|
|
optimize it into just op0 if op0's range is known to be a subset of
|
|
[-op1min + 1, op1min - 1] for signed and [0, op1min - 1] for unsigned
|
|
modulo. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_div_or_mod_using_ranges
|
|
(gimple_stmt_iterator *gsi,
|
|
gimple *stmt)
|
|
{
|
|
enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
|
|
tree val = NULL;
|
|
tree op0 = gimple_assign_rhs1 (stmt);
|
|
tree op1 = gimple_assign_rhs2 (stmt);
|
|
tree op0min = NULL_TREE, op0max = NULL_TREE;
|
|
tree op1min = op1;
|
|
int_range_max vr;
|
|
|
|
if (TREE_CODE (op0) == INTEGER_CST)
|
|
{
|
|
op0min = op0;
|
|
op0max = op0;
|
|
}
|
|
else
|
|
{
|
|
if (!query->range_of_expr (vr, op0, stmt))
|
|
vr.set_varying (TREE_TYPE (op0));
|
|
if (!vr.varying_p () && !vr.undefined_p ())
|
|
{
|
|
tree type = vr.type ();
|
|
op0min = wide_int_to_tree (type, vr.lower_bound ());
|
|
op0max = wide_int_to_tree (type, vr.upper_bound ());
|
|
}
|
|
}
|
|
|
|
if (rhs_code == TRUNC_MOD_EXPR
|
|
&& TREE_CODE (op1) == SSA_NAME)
|
|
{
|
|
int_range_max vr1;
|
|
if (!query->range_of_expr (vr1, op1, stmt))
|
|
vr1.set_varying (TREE_TYPE (op1));
|
|
if (!vr1.varying_p () && !vr1.undefined_p ())
|
|
op1min = wide_int_to_tree (vr1.type (), vr1.lower_bound ());
|
|
}
|
|
if (rhs_code == TRUNC_MOD_EXPR
|
|
&& TREE_CODE (op1min) == INTEGER_CST
|
|
&& tree_int_cst_sgn (op1min) == 1
|
|
&& op0max
|
|
&& tree_int_cst_lt (op0max, op1min))
|
|
{
|
|
if (TYPE_UNSIGNED (TREE_TYPE (op0))
|
|
|| tree_int_cst_sgn (op0min) >= 0
|
|
|| tree_int_cst_lt (fold_unary (NEGATE_EXPR, TREE_TYPE (op1min), op1min),
|
|
op0min))
|
|
{
|
|
/* If op0 already has the range op0 % op1 has,
|
|
then TRUNC_MOD_EXPR won't change anything. */
|
|
gimple_assign_set_rhs_from_tree (gsi, op0);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (TREE_CODE (op0) != SSA_NAME)
|
|
return false;
|
|
|
|
if (!integer_pow2p (op1))
|
|
{
|
|
/* X % -Y can be only optimized into X % Y either if
|
|
X is not INT_MIN, or Y is not -1. Fold it now, as after
|
|
remove_range_assertions the range info might be not available
|
|
anymore. */
|
|
if (rhs_code == TRUNC_MOD_EXPR
|
|
&& fold_stmt (gsi, follow_single_use_edges))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
if (TYPE_UNSIGNED (TREE_TYPE (op0)))
|
|
val = integer_one_node;
|
|
else
|
|
{
|
|
tree zero = build_zero_cst (TREE_TYPE (op0));
|
|
val = fold_cond_with_ops (GE_EXPR, op0, zero, stmt);
|
|
}
|
|
|
|
if (val && integer_onep (val))
|
|
{
|
|
tree t;
|
|
|
|
if (rhs_code == TRUNC_DIV_EXPR)
|
|
{
|
|
t = build_int_cst (integer_type_node, tree_log2 (op1));
|
|
gimple_assign_set_rhs_code (stmt, RSHIFT_EXPR);
|
|
gimple_assign_set_rhs1 (stmt, op0);
|
|
gimple_assign_set_rhs2 (stmt, t);
|
|
}
|
|
else
|
|
{
|
|
t = build_int_cst (TREE_TYPE (op1), 1);
|
|
t = int_const_binop (MINUS_EXPR, op1, t);
|
|
t = fold_convert (TREE_TYPE (op0), t);
|
|
|
|
gimple_assign_set_rhs_code (stmt, BIT_AND_EXPR);
|
|
gimple_assign_set_rhs1 (stmt, op0);
|
|
gimple_assign_set_rhs2 (stmt, t);
|
|
}
|
|
|
|
update_stmt (stmt);
|
|
fold_stmt (gsi, follow_single_use_edges);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Simplify a min or max if the ranges of the two operands are
|
|
disjoint. Return true if we do simplify. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_min_or_max_using_ranges
|
|
(gimple_stmt_iterator *gsi,
|
|
gimple *stmt)
|
|
{
|
|
tree op0 = gimple_assign_rhs1 (stmt);
|
|
tree op1 = gimple_assign_rhs2 (stmt);
|
|
tree val;
|
|
|
|
val = fold_cond_with_ops (LE_EXPR, op0, op1, stmt);
|
|
if (!val)
|
|
val = fold_cond_with_ops (LT_EXPR, op0, op1, stmt);
|
|
|
|
if (val)
|
|
{
|
|
/* VAL == TRUE -> OP0 < or <= op1
|
|
VAL == FALSE -> OP0 > or >= op1. */
|
|
tree res = ((gimple_assign_rhs_code (stmt) == MAX_EXPR)
|
|
== integer_zerop (val)) ? op0 : op1;
|
|
gimple_assign_set_rhs_from_tree (gsi, res);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* If the operand to an ABS_EXPR is >= 0, then eliminate the
|
|
ABS_EXPR. If the operand is <= 0, then simplify the
|
|
ABS_EXPR into a NEGATE_EXPR. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_abs_using_ranges (gimple_stmt_iterator *gsi,
|
|
gimple *stmt)
|
|
{
|
|
tree op = gimple_assign_rhs1 (stmt);
|
|
tree zero = build_zero_cst (TREE_TYPE (op));
|
|
tree val = fold_cond_with_ops (LE_EXPR, op, zero, stmt);
|
|
|
|
if (!val)
|
|
{
|
|
/* The range is neither <= 0 nor > 0. Now see if it is
|
|
either < 0 or >= 0. */
|
|
val = fold_cond_with_ops (LT_EXPR, op, zero, stmt);
|
|
}
|
|
if (val)
|
|
{
|
|
gimple_assign_set_rhs1 (stmt, op);
|
|
if (integer_zerop (val))
|
|
gimple_assign_set_rhs_code (stmt, SSA_NAME);
|
|
else
|
|
gimple_assign_set_rhs_code (stmt, NEGATE_EXPR);
|
|
update_stmt (stmt);
|
|
fold_stmt (gsi, follow_single_use_edges);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* irange wrapper for wi_set_zero_nonzero_bits.
|
|
|
|
Return TRUE if VR was a constant range and we were able to compute
|
|
the bit masks. */
|
|
|
|
static bool
|
|
vr_set_zero_nonzero_bits (const tree expr_type,
|
|
const irange *vr,
|
|
wide_int *may_be_nonzero,
|
|
wide_int *must_be_nonzero)
|
|
{
|
|
if (vr->varying_p () || vr->undefined_p ())
|
|
{
|
|
*may_be_nonzero = wi::minus_one (TYPE_PRECISION (expr_type));
|
|
*must_be_nonzero = wi::zero (TYPE_PRECISION (expr_type));
|
|
return false;
|
|
}
|
|
wi_set_zero_nonzero_bits (expr_type, vr->lower_bound (), vr->upper_bound (),
|
|
*may_be_nonzero, *must_be_nonzero);
|
|
return true;
|
|
}
|
|
|
|
/* Optimize away redundant BIT_AND_EXPR and BIT_IOR_EXPR.
|
|
If all the bits that are being cleared by & are already
|
|
known to be zero from VR, or all the bits that are being
|
|
set by | are already known to be one from VR, the bit
|
|
operation is redundant. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_bit_ops_using_ranges
|
|
(gimple_stmt_iterator *gsi,
|
|
gimple *stmt)
|
|
{
|
|
tree op0 = gimple_assign_rhs1 (stmt);
|
|
tree op1 = gimple_assign_rhs2 (stmt);
|
|
tree op = NULL_TREE;
|
|
int_range_max vr0, vr1;
|
|
wide_int may_be_nonzero0, may_be_nonzero1;
|
|
wide_int must_be_nonzero0, must_be_nonzero1;
|
|
wide_int mask;
|
|
|
|
if (!query->range_of_expr (vr0, op0, stmt)
|
|
|| vr0.undefined_p ())
|
|
return false;
|
|
if (!query->range_of_expr (vr1, op1, stmt)
|
|
|| vr1.undefined_p ())
|
|
return false;
|
|
|
|
if (!vr_set_zero_nonzero_bits (TREE_TYPE (op0), &vr0, &may_be_nonzero0,
|
|
&must_be_nonzero0))
|
|
return false;
|
|
if (!vr_set_zero_nonzero_bits (TREE_TYPE (op1), &vr1, &may_be_nonzero1,
|
|
&must_be_nonzero1))
|
|
return false;
|
|
|
|
switch (gimple_assign_rhs_code (stmt))
|
|
{
|
|
case BIT_AND_EXPR:
|
|
mask = wi::bit_and_not (may_be_nonzero0, must_be_nonzero1);
|
|
if (mask == 0)
|
|
{
|
|
op = op0;
|
|
break;
|
|
}
|
|
mask = wi::bit_and_not (may_be_nonzero1, must_be_nonzero0);
|
|
if (mask == 0)
|
|
{
|
|
op = op1;
|
|
break;
|
|
}
|
|
break;
|
|
case BIT_IOR_EXPR:
|
|
mask = wi::bit_and_not (may_be_nonzero0, must_be_nonzero1);
|
|
if (mask == 0)
|
|
{
|
|
op = op1;
|
|
break;
|
|
}
|
|
mask = wi::bit_and_not (may_be_nonzero1, must_be_nonzero0);
|
|
if (mask == 0)
|
|
{
|
|
op = op0;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
gcc_unreachable ();
|
|
}
|
|
|
|
if (op == NULL_TREE)
|
|
return false;
|
|
|
|
gimple_assign_set_rhs_with_ops (gsi, TREE_CODE (op), op);
|
|
update_stmt (gsi_stmt (*gsi));
|
|
return true;
|
|
}
|
|
|
|
/* We are comparing trees OP0 and OP1 using COND_CODE. OP0 has
|
|
a known value range VR.
|
|
|
|
If there is one and only one value which will satisfy the
|
|
conditional, then return that value. Else return NULL.
|
|
|
|
If signed overflow must be undefined for the value to satisfy
|
|
the conditional, then set *STRICT_OVERFLOW_P to true. */
|
|
|
|
static tree
|
|
test_for_singularity (enum tree_code cond_code, tree op0,
|
|
tree op1, const irange *vr)
|
|
{
|
|
tree min = NULL;
|
|
tree max = NULL;
|
|
|
|
/* Extract minimum/maximum values which satisfy the conditional as it was
|
|
written. */
|
|
if (cond_code == LE_EXPR || cond_code == LT_EXPR)
|
|
{
|
|
min = TYPE_MIN_VALUE (TREE_TYPE (op0));
|
|
|
|
max = op1;
|
|
if (cond_code == LT_EXPR)
|
|
{
|
|
tree one = build_int_cst (TREE_TYPE (op0), 1);
|
|
max = fold_build2 (MINUS_EXPR, TREE_TYPE (op0), max, one);
|
|
/* Signal to compare_values_warnv this expr doesn't overflow. */
|
|
if (EXPR_P (max))
|
|
suppress_warning (max, OPT_Woverflow);
|
|
}
|
|
}
|
|
else if (cond_code == GE_EXPR || cond_code == GT_EXPR)
|
|
{
|
|
max = TYPE_MAX_VALUE (TREE_TYPE (op0));
|
|
|
|
min = op1;
|
|
if (cond_code == GT_EXPR)
|
|
{
|
|
tree one = build_int_cst (TREE_TYPE (op0), 1);
|
|
min = fold_build2 (PLUS_EXPR, TREE_TYPE (op0), min, one);
|
|
/* Signal to compare_values_warnv this expr doesn't overflow. */
|
|
if (EXPR_P (min))
|
|
suppress_warning (min, OPT_Woverflow);
|
|
}
|
|
}
|
|
|
|
/* Now refine the minimum and maximum values using any
|
|
value range information we have for op0. */
|
|
if (min && max)
|
|
{
|
|
tree type = TREE_TYPE (op0);
|
|
tree tmin = wide_int_to_tree (type, vr->lower_bound ());
|
|
tree tmax = wide_int_to_tree (type, vr->upper_bound ());
|
|
if (compare_values (tmin, min) == 1)
|
|
min = tmin;
|
|
if (compare_values (tmax, max) == -1)
|
|
max = tmax;
|
|
|
|
/* If the new min/max values have converged to a single value,
|
|
then there is only one value which can satisfy the condition,
|
|
return that value. */
|
|
if (operand_equal_p (min, max, 0) && is_gimple_min_invariant (min))
|
|
return min;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Return whether the value range *VR fits in an integer type specified
|
|
by PRECISION and UNSIGNED_P. */
|
|
|
|
bool
|
|
range_fits_type_p (const irange *vr,
|
|
unsigned dest_precision, signop dest_sgn)
|
|
{
|
|
tree src_type;
|
|
unsigned src_precision;
|
|
widest_int tem;
|
|
signop src_sgn;
|
|
|
|
/* Now we can only handle ranges with constant bounds. */
|
|
if (vr->undefined_p () || vr->varying_p ())
|
|
return false;
|
|
|
|
/* We can only handle integral and pointer types. */
|
|
src_type = vr->type ();
|
|
if (!INTEGRAL_TYPE_P (src_type)
|
|
&& !POINTER_TYPE_P (src_type))
|
|
return false;
|
|
|
|
/* An extension is fine unless VR is SIGNED and dest_sgn is UNSIGNED,
|
|
and so is an identity transform. */
|
|
src_precision = TYPE_PRECISION (src_type);
|
|
src_sgn = TYPE_SIGN (src_type);
|
|
if ((src_precision < dest_precision
|
|
&& !(dest_sgn == UNSIGNED && src_sgn == SIGNED))
|
|
|| (src_precision == dest_precision && src_sgn == dest_sgn))
|
|
return true;
|
|
|
|
wide_int vrmin = vr->lower_bound ();
|
|
wide_int vrmax = vr->upper_bound ();
|
|
|
|
/* For sign changes, the MSB of the wide_int has to be clear.
|
|
An unsigned value with its MSB set cannot be represented by
|
|
a signed wide_int, while a negative value cannot be represented
|
|
by an unsigned wide_int. */
|
|
if (src_sgn != dest_sgn
|
|
&& (wi::lts_p (vrmin, 0) || wi::lts_p (vrmax, 0)))
|
|
return false;
|
|
|
|
/* Then we can perform the conversion on both ends and compare
|
|
the result for equality. */
|
|
signop sign = TYPE_SIGN (vr->type ());
|
|
tem = wi::ext (widest_int::from (vrmin, sign), dest_precision, dest_sgn);
|
|
if (tem != widest_int::from (vrmin, sign))
|
|
return false;
|
|
tem = wi::ext (widest_int::from (vrmax, sign), dest_precision, dest_sgn);
|
|
if (tem != widest_int::from (vrmax, sign))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Clear edge E of EDGE_EXECUTABLE (it is unexecutable). If it wasn't
|
|
// previously clear, propagate to successor blocks if appropriate.
|
|
|
|
void
|
|
simplify_using_ranges::set_and_propagate_unexecutable (edge e)
|
|
{
|
|
// If not_executable is already set, we're done.
|
|
// This works in the absence of a flag as well.
|
|
if ((e->flags & m_not_executable_flag) == m_not_executable_flag)
|
|
return;
|
|
|
|
e->flags |= m_not_executable_flag;
|
|
m_flag_set_edges.safe_push (e);
|
|
|
|
// Check if the destination block needs to propagate the property.
|
|
basic_block bb = e->dest;
|
|
|
|
// If any incoming edge is executable, we are done.
|
|
edge_iterator ei;
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
if ((e->flags & m_not_executable_flag) == 0)
|
|
return;
|
|
|
|
// This block is also unexecutable, propagate to all exit edges as well.
|
|
FOR_EACH_EDGE (e, ei, bb->succs)
|
|
set_and_propagate_unexecutable (e);
|
|
}
|
|
|
|
/* If COND can be folded entirely as TRUE or FALSE, rewrite the
|
|
conditional as such, and return TRUE. */
|
|
|
|
bool
|
|
simplify_using_ranges::fold_cond (gcond *cond)
|
|
{
|
|
int_range_max r;
|
|
if (query->range_of_stmt (r, cond) && r.singleton_p ())
|
|
{
|
|
// COND has already been folded if arguments are constant.
|
|
if (TREE_CODE (gimple_cond_lhs (cond)) != SSA_NAME
|
|
&& TREE_CODE (gimple_cond_rhs (cond)) != SSA_NAME)
|
|
return false;
|
|
if (dump_file)
|
|
{
|
|
fprintf (dump_file, "Folding predicate ");
|
|
print_gimple_expr (dump_file, cond, 0);
|
|
fprintf (dump_file, " to ");
|
|
}
|
|
edge e0 = EDGE_SUCC (gimple_bb (cond), 0);
|
|
edge e1 = EDGE_SUCC (gimple_bb (cond), 1);
|
|
if (r.zero_p ())
|
|
{
|
|
if (dump_file)
|
|
fprintf (dump_file, "0\n");
|
|
gimple_cond_make_false (cond);
|
|
if (e0->flags & EDGE_TRUE_VALUE)
|
|
set_and_propagate_unexecutable (e0);
|
|
else
|
|
set_and_propagate_unexecutable (e1);
|
|
}
|
|
else
|
|
{
|
|
if (dump_file)
|
|
fprintf (dump_file, "1\n");
|
|
gimple_cond_make_true (cond);
|
|
if (e0->flags & EDGE_FALSE_VALUE)
|
|
set_and_propagate_unexecutable (e0);
|
|
else
|
|
set_and_propagate_unexecutable (e1);
|
|
}
|
|
update_stmt (cond);
|
|
return true;
|
|
}
|
|
|
|
// FIXME: Audit the code below and make sure it never finds anything.
|
|
edge taken_edge;
|
|
legacy_fold_cond (cond, &taken_edge);
|
|
|
|
if (taken_edge)
|
|
{
|
|
if (taken_edge->flags & EDGE_TRUE_VALUE)
|
|
{
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
fprintf (dump_file, "\nVRP Predicate evaluates to: 1\n");
|
|
gimple_cond_make_true (cond);
|
|
}
|
|
else if (taken_edge->flags & EDGE_FALSE_VALUE)
|
|
{
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
fprintf (dump_file, "\nVRP Predicate evaluates to: 0\n");
|
|
gimple_cond_make_false (cond);
|
|
}
|
|
else
|
|
gcc_unreachable ();
|
|
update_stmt (cond);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Simplify a conditional using a relational operator to an equality
|
|
test if the range information indicates only one value can satisfy
|
|
the original conditional. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_cond_using_ranges_1 (gcond *stmt)
|
|
{
|
|
tree op0 = gimple_cond_lhs (stmt);
|
|
tree op1 = gimple_cond_rhs (stmt);
|
|
enum tree_code cond_code = gimple_cond_code (stmt);
|
|
|
|
if (fold_cond (stmt))
|
|
return true;
|
|
|
|
if (simplify_compare_using_ranges_1 (cond_code, op0, op1, stmt))
|
|
{
|
|
if (dump_file)
|
|
{
|
|
fprintf (dump_file, "Simplified relational ");
|
|
print_gimple_stmt (dump_file, stmt, 0);
|
|
fprintf (dump_file, " into ");
|
|
}
|
|
|
|
gimple_cond_set_code (stmt, cond_code);
|
|
gimple_cond_set_lhs (stmt, op0);
|
|
gimple_cond_set_rhs (stmt, op1);
|
|
|
|
update_stmt (stmt);
|
|
|
|
if (dump_file)
|
|
{
|
|
print_gimple_stmt (dump_file, stmt, 0);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Like simplify_cond_using_ranges_1 but for assignments rather
|
|
than GIMPLE_COND. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_compare_assign_using_ranges_1
|
|
(gimple_stmt_iterator *gsi,
|
|
gimple *stmt)
|
|
{
|
|
enum tree_code code = gimple_assign_rhs_code (stmt);
|
|
tree op0 = gimple_assign_rhs1 (stmt);
|
|
tree op1 = gimple_assign_rhs2 (stmt);
|
|
gcc_assert (TREE_CODE_CLASS (code) == tcc_comparison);
|
|
bool happened = false;
|
|
|
|
if (simplify_compare_using_ranges_1 (code, op0, op1, stmt))
|
|
{
|
|
if (dump_file)
|
|
{
|
|
fprintf (dump_file, "Simplified relational ");
|
|
print_gimple_stmt (dump_file, stmt, 0);
|
|
fprintf (dump_file, " into ");
|
|
}
|
|
|
|
gimple_assign_set_rhs_code (stmt, code);
|
|
gimple_assign_set_rhs1 (stmt, op0);
|
|
gimple_assign_set_rhs2 (stmt, op1);
|
|
|
|
update_stmt (stmt);
|
|
|
|
if (dump_file)
|
|
{
|
|
print_gimple_stmt (dump_file, stmt, 0);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
happened = true;
|
|
}
|
|
|
|
/* Transform EQ_EXPR, NE_EXPR into BIT_XOR_EXPR or identity
|
|
if the RHS is zero or one, and the LHS are known to be boolean
|
|
values. */
|
|
if ((code == EQ_EXPR || code == NE_EXPR)
|
|
&& INTEGRAL_TYPE_P (TREE_TYPE (op0))
|
|
&& simplify_truth_ops_using_ranges (gsi, stmt))
|
|
happened = true;
|
|
|
|
return happened;
|
|
}
|
|
|
|
/* Try to simplify OP0 COND_CODE OP1 using a relational operator to an
|
|
equality test if the range information indicates only one value can
|
|
satisfy the original conditional. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_compare_using_ranges_1 (tree_code &cond_code, tree &op0, tree &op1, gimple *stmt)
|
|
{
|
|
bool happened = false;
|
|
if (cond_code != NE_EXPR
|
|
&& cond_code != EQ_EXPR
|
|
&& TREE_CODE (op0) == SSA_NAME
|
|
&& INTEGRAL_TYPE_P (TREE_TYPE (op0))
|
|
&& is_gimple_min_invariant (op1))
|
|
{
|
|
int_range_max vr;
|
|
|
|
if (!query->range_of_expr (vr, op0, stmt))
|
|
vr.set_undefined ();
|
|
|
|
/* If we have range information for OP0, then we might be
|
|
able to simplify this conditional. */
|
|
if (!vr.undefined_p () && !vr.varying_p ())
|
|
{
|
|
tree new_tree = test_for_singularity (cond_code, op0, op1, &vr);
|
|
if (new_tree)
|
|
{
|
|
cond_code = EQ_EXPR;
|
|
op1 = new_tree;
|
|
happened = true;
|
|
}
|
|
|
|
/* Try again after inverting the condition. We only deal
|
|
with integral types here, so no need to worry about
|
|
issues with inverting FP comparisons. */
|
|
new_tree = test_for_singularity
|
|
(invert_tree_comparison (cond_code, false),
|
|
op0, op1, &vr);
|
|
if (new_tree)
|
|
{
|
|
cond_code = NE_EXPR;
|
|
op1 = new_tree;
|
|
happened = true;
|
|
}
|
|
}
|
|
}
|
|
// Try to simplify casted conditions.
|
|
if (simplify_casted_compare (cond_code, op0, op1))
|
|
happened = true;
|
|
return happened;
|
|
}
|
|
|
|
/* Simplify OP0 code OP1 when OP1 is a constant and OP0 was a SSA_NAME
|
|
defined by a type conversion. Replacing OP0 with RHS of the type conversion.
|
|
Doing so makes the conversion dead which helps subsequent passes. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_casted_compare (tree_code &, tree &op0, tree &op1)
|
|
{
|
|
|
|
/* If we have a comparison of an SSA_NAME (OP0) against a constant,
|
|
see if OP0 was set by a type conversion where the source of
|
|
the conversion is another SSA_NAME with a range that fits
|
|
into the range of OP0's type.
|
|
|
|
If so, the conversion is redundant as the earlier SSA_NAME can be
|
|
used for the comparison directly if we just massage the constant in the
|
|
comparison. */
|
|
if (TREE_CODE (op0) == SSA_NAME
|
|
&& TREE_CODE (op1) == INTEGER_CST)
|
|
{
|
|
gimple *def_stmt = SSA_NAME_DEF_STMT (op0);
|
|
tree innerop;
|
|
|
|
if (!is_gimple_assign (def_stmt))
|
|
return false;
|
|
|
|
switch (gimple_assign_rhs_code (def_stmt))
|
|
{
|
|
CASE_CONVERT:
|
|
innerop = gimple_assign_rhs1 (def_stmt);
|
|
break;
|
|
case VIEW_CONVERT_EXPR:
|
|
innerop = TREE_OPERAND (gimple_assign_rhs1 (def_stmt), 0);
|
|
if (!INTEGRAL_TYPE_P (TREE_TYPE (innerop)))
|
|
return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (TREE_CODE (innerop) == SSA_NAME
|
|
&& !POINTER_TYPE_P (TREE_TYPE (innerop))
|
|
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (innerop)
|
|
&& desired_pro_or_demotion_p (TREE_TYPE (innerop), TREE_TYPE (op0)))
|
|
{
|
|
int_range_max vr;
|
|
|
|
if (query->range_of_expr (vr, innerop)
|
|
&& !vr.varying_p ()
|
|
&& !vr.undefined_p ()
|
|
&& range_fits_type_p (&vr,
|
|
TYPE_PRECISION (TREE_TYPE (op0)),
|
|
TYPE_SIGN (TREE_TYPE (op0)))
|
|
&& int_fits_type_p (op1, TREE_TYPE (innerop)))
|
|
{
|
|
tree newconst = fold_convert (TREE_TYPE (innerop), op1);
|
|
op0 = innerop;
|
|
op1 = newconst;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Simplify a switch statement using the value range of the switch
|
|
argument. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_switch_using_ranges (gswitch *stmt)
|
|
{
|
|
tree op = gimple_switch_index (stmt);
|
|
tree type = TREE_TYPE (op);
|
|
int_range_max op_range (type);
|
|
int_range_max default_range (type);
|
|
auto_vec<unsigned> cases;
|
|
cases.truncate (0);
|
|
edge e;
|
|
switch_update su;
|
|
|
|
// Abort if we don't have a useful range for the switch index.
|
|
if (!query->range_of_expr (op_range, op, stmt)
|
|
|| op_range.varying_p () || op_range.undefined_p ())
|
|
return false;
|
|
|
|
// Default range starts with full known range of op.
|
|
default_range = op_range;
|
|
edge default_edge = gimple_switch_default_edge (cfun, stmt);
|
|
|
|
unsigned x, lim = gimple_switch_num_labels (stmt);
|
|
for (x = 1; x < lim; x++)
|
|
{
|
|
e = gimple_switch_edge (cfun, stmt, x);
|
|
tree label = gimple_switch_label (stmt, x);
|
|
|
|
// If this edge is the same as the default edge, do nothing else.
|
|
if (e == default_edge)
|
|
continue;
|
|
// Ada sometimes has mismatched labels and index. Just bail.
|
|
if (TREE_TYPE (CASE_LOW (label)) != type)
|
|
return false;
|
|
|
|
wide_int low = wi::to_wide (CASE_LOW (label));
|
|
wide_int high;
|
|
// Singleton cases have no CASE_HIGH.
|
|
tree tree_high = CASE_HIGH (label);
|
|
if (tree_high)
|
|
high = wi::to_wide (tree_high);
|
|
else
|
|
high = low;
|
|
|
|
// If the case range is fully contained in op_range, leave the
|
|
// case as it is, otherwise adjust the labels.
|
|
int_range_max case_range (type, low, high);
|
|
if (case_range.intersect (op_range))
|
|
{
|
|
// If none of the label is in op_range, skip this label.
|
|
if (case_range.undefined_p ())
|
|
continue;
|
|
|
|
// Part of the label is in op_range, but not all of it. CASE_RANGE
|
|
// contains the part that is. Adjust the case range to
|
|
// the new min/max.
|
|
if (case_range.lower_bound () != low)
|
|
CASE_LOW (label) = wide_int_to_tree (type,
|
|
case_range.lower_bound ());
|
|
if (case_range.singleton_p ())
|
|
CASE_HIGH (label) = NULL_TREE;
|
|
else
|
|
if (case_range.upper_bound () != high)
|
|
CASE_HIGH (label) = wide_int_to_tree (type,
|
|
case_range.upper_bound ());
|
|
}
|
|
// Add case label to the keep list.
|
|
cases.safe_push (x);
|
|
// Remove case_range from needing to be handled by the default.
|
|
case_range.invert ();
|
|
default_range.intersect (case_range);
|
|
}
|
|
|
|
// An undefined DEFAULT range means the current default case is not needed.
|
|
unsigned idx = default_range.undefined_p () ? 0 : 1;
|
|
unsigned vec_size = cases.length () + idx;
|
|
if (vec_size == lim)
|
|
return false;
|
|
|
|
tree vec2 = make_tree_vec (vec_size);
|
|
// Add default label if there is one.
|
|
if (idx)
|
|
{
|
|
TREE_VEC_ELT (vec2, 0) = gimple_switch_default_label (stmt);
|
|
e = gimple_switch_edge (cfun, stmt, 0);
|
|
e->aux = (void *)-1;
|
|
}
|
|
|
|
for (x = 0; x < cases.length (); x++)
|
|
{
|
|
unsigned swi = cases[x];
|
|
TREE_VEC_ELT (vec2, idx++) = gimple_switch_label (stmt, swi);
|
|
e = gimple_switch_edge (cfun, stmt, swi);
|
|
e->aux = (void *)-1;
|
|
}
|
|
|
|
/* Queue not needed edges for later removal. */
|
|
edge_iterator ei;
|
|
FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->succs)
|
|
{
|
|
if (e->aux == (void *)-1)
|
|
{
|
|
e->aux = NULL;
|
|
continue;
|
|
}
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "removing unreachable case label\n");
|
|
}
|
|
to_remove_edges.safe_push (e);
|
|
set_and_propagate_unexecutable (e);
|
|
e->flags &= ~EDGE_EXECUTABLE;
|
|
e->flags |= EDGE_IGNORE;
|
|
}
|
|
|
|
/* And queue an update for the stmt. */
|
|
su.stmt = stmt;
|
|
su.vec = vec2;
|
|
to_update_switch_stmts.safe_push (su);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
simplify_using_ranges::cleanup_edges_and_switches (void)
|
|
{
|
|
int i;
|
|
edge e;
|
|
switch_update *su;
|
|
|
|
/* Clear any edges marked as not executable. */
|
|
if (m_not_executable_flag)
|
|
{
|
|
FOR_EACH_VEC_ELT (m_flag_set_edges, i, e)
|
|
e->flags &= ~m_not_executable_flag;
|
|
}
|
|
/* Remove dead edges from SWITCH_EXPR optimization. This leaves the
|
|
CFG in a broken state and requires a cfg_cleanup run. */
|
|
FOR_EACH_VEC_ELT (to_remove_edges, i, e)
|
|
remove_edge (e);
|
|
|
|
/* Update SWITCH_EXPR case label vector. */
|
|
FOR_EACH_VEC_ELT (to_update_switch_stmts, i, su)
|
|
{
|
|
size_t j;
|
|
size_t n = TREE_VEC_LENGTH (su->vec);
|
|
tree label;
|
|
gimple_switch_set_num_labels (su->stmt, n);
|
|
for (j = 0; j < n; j++)
|
|
gimple_switch_set_label (su->stmt, j, TREE_VEC_ELT (su->vec, j));
|
|
/* As we may have replaced the default label with a regular one
|
|
make sure to make it a real default label again. This ensures
|
|
optimal expansion. */
|
|
label = gimple_switch_label (su->stmt, 0);
|
|
CASE_LOW (label) = NULL_TREE;
|
|
CASE_HIGH (label) = NULL_TREE;
|
|
}
|
|
|
|
if (!to_remove_edges.is_empty ())
|
|
{
|
|
free_dominance_info (CDI_DOMINATORS);
|
|
loops_state_set (LOOPS_NEED_FIXUP);
|
|
}
|
|
|
|
to_remove_edges.release ();
|
|
to_update_switch_stmts.release ();
|
|
}
|
|
|
|
/* Simplify an integral conversion from an SSA name in STMT. */
|
|
|
|
static bool
|
|
simplify_conversion_using_ranges (gimple_stmt_iterator *gsi, gimple *stmt)
|
|
{
|
|
tree innerop, middleop, finaltype;
|
|
gimple *def_stmt;
|
|
signop inner_sgn, middle_sgn, final_sgn;
|
|
unsigned inner_prec, middle_prec, final_prec;
|
|
widest_int innermin, innermed, innermax, middlemin, middlemed, middlemax;
|
|
|
|
finaltype = TREE_TYPE (gimple_assign_lhs (stmt));
|
|
if (!INTEGRAL_TYPE_P (finaltype))
|
|
return false;
|
|
middleop = gimple_assign_rhs1 (stmt);
|
|
def_stmt = SSA_NAME_DEF_STMT (middleop);
|
|
if (!is_gimple_assign (def_stmt)
|
|
|| !CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt)))
|
|
return false;
|
|
innerop = gimple_assign_rhs1 (def_stmt);
|
|
if (TREE_CODE (innerop) != SSA_NAME
|
|
|| SSA_NAME_OCCURS_IN_ABNORMAL_PHI (innerop))
|
|
return false;
|
|
|
|
/* Get the value-range of the inner operand. Use global ranges in
|
|
case innerop was created during substitute-and-fold. */
|
|
wide_int imin, imax;
|
|
int_range_max vr;
|
|
if (!INTEGRAL_TYPE_P (TREE_TYPE (innerop)))
|
|
return false;
|
|
get_range_query (cfun)->range_of_expr (vr, innerop, stmt);
|
|
if (vr.undefined_p () || vr.varying_p ())
|
|
return false;
|
|
innermin = widest_int::from (vr.lower_bound (), TYPE_SIGN (TREE_TYPE (innerop)));
|
|
innermax = widest_int::from (vr.upper_bound (), TYPE_SIGN (TREE_TYPE (innerop)));
|
|
|
|
/* Simulate the conversion chain to check if the result is equal if
|
|
the middle conversion is removed. */
|
|
inner_prec = TYPE_PRECISION (TREE_TYPE (innerop));
|
|
middle_prec = TYPE_PRECISION (TREE_TYPE (middleop));
|
|
final_prec = TYPE_PRECISION (finaltype);
|
|
|
|
/* If the first conversion is not injective, the second must not
|
|
be widening. */
|
|
if (wi::gtu_p (innermax - innermin,
|
|
wi::mask <widest_int> (middle_prec, false))
|
|
&& middle_prec < final_prec)
|
|
return false;
|
|
/* We also want a medium value so that we can track the effect that
|
|
narrowing conversions with sign change have. */
|
|
inner_sgn = TYPE_SIGN (TREE_TYPE (innerop));
|
|
if (inner_sgn == UNSIGNED)
|
|
innermed = wi::shifted_mask <widest_int> (1, inner_prec - 1, false);
|
|
else
|
|
innermed = 0;
|
|
if (wi::cmp (innermin, innermed, inner_sgn) >= 0
|
|
|| wi::cmp (innermed, innermax, inner_sgn) >= 0)
|
|
innermed = innermin;
|
|
|
|
middle_sgn = TYPE_SIGN (TREE_TYPE (middleop));
|
|
middlemin = wi::ext (innermin, middle_prec, middle_sgn);
|
|
middlemed = wi::ext (innermed, middle_prec, middle_sgn);
|
|
middlemax = wi::ext (innermax, middle_prec, middle_sgn);
|
|
|
|
/* Require that the final conversion applied to both the original
|
|
and the intermediate range produces the same result. */
|
|
final_sgn = TYPE_SIGN (finaltype);
|
|
if (wi::ext (middlemin, final_prec, final_sgn)
|
|
!= wi::ext (innermin, final_prec, final_sgn)
|
|
|| wi::ext (middlemed, final_prec, final_sgn)
|
|
!= wi::ext (innermed, final_prec, final_sgn)
|
|
|| wi::ext (middlemax, final_prec, final_sgn)
|
|
!= wi::ext (innermax, final_prec, final_sgn))
|
|
return false;
|
|
|
|
gimple_assign_set_rhs1 (stmt, innerop);
|
|
fold_stmt (gsi, follow_single_use_edges);
|
|
return true;
|
|
}
|
|
|
|
/* Simplify a conversion from integral SSA name to float in STMT. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_float_conversion_using_ranges
|
|
(gimple_stmt_iterator *gsi,
|
|
gimple *stmt)
|
|
{
|
|
tree rhs1 = gimple_assign_rhs1 (stmt);
|
|
int_range_max vr;
|
|
scalar_float_mode fltmode
|
|
= SCALAR_FLOAT_TYPE_MODE (TREE_TYPE (gimple_assign_lhs (stmt)));
|
|
scalar_int_mode mode;
|
|
tree tem;
|
|
gassign *conv;
|
|
|
|
/* We can only handle constant ranges. */
|
|
if (!query->range_of_expr (vr, rhs1, stmt)
|
|
|| vr.varying_p ()
|
|
|| vr.undefined_p ())
|
|
return false;
|
|
|
|
/* The code below doesn't work for large/huge _BitInt, nor is really
|
|
needed for those, bitint lowering does use ranges already. */
|
|
if (TREE_CODE (TREE_TYPE (rhs1)) == BITINT_TYPE
|
|
&& TYPE_MODE (TREE_TYPE (rhs1)) == BLKmode)
|
|
return false;
|
|
/* First check if we can use a signed type in place of an unsigned. */
|
|
scalar_int_mode rhs_mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (rhs1));
|
|
if (TYPE_UNSIGNED (TREE_TYPE (rhs1))
|
|
&& can_float_p (fltmode, rhs_mode, 0) != CODE_FOR_nothing
|
|
&& range_fits_type_p (&vr, TYPE_PRECISION (TREE_TYPE (rhs1)), SIGNED))
|
|
mode = rhs_mode;
|
|
/* If we can do the conversion in the current input mode do nothing. */
|
|
else if (can_float_p (fltmode, rhs_mode,
|
|
TYPE_UNSIGNED (TREE_TYPE (rhs1))) != CODE_FOR_nothing)
|
|
return false;
|
|
/* Otherwise search for a mode we can use, starting from the narrowest
|
|
integer mode available. */
|
|
else
|
|
{
|
|
mode = NARROWEST_INT_MODE;
|
|
for (;;)
|
|
{
|
|
/* If we cannot do a signed conversion to float from mode
|
|
or if the value-range does not fit in the signed type
|
|
try with a wider mode. */
|
|
if (can_float_p (fltmode, mode, 0) != CODE_FOR_nothing
|
|
&& range_fits_type_p (&vr, GET_MODE_PRECISION (mode), SIGNED))
|
|
break;
|
|
|
|
/* But do not widen the input. Instead leave that to the
|
|
optabs expansion code. */
|
|
if (!GET_MODE_WIDER_MODE (mode).exists (&mode)
|
|
|| GET_MODE_PRECISION (mode) > TYPE_PRECISION (TREE_TYPE (rhs1)))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* It works, insert a truncation or sign-change before the
|
|
float conversion. */
|
|
tem = make_ssa_name (build_nonstandard_integer_type
|
|
(GET_MODE_PRECISION (mode), 0));
|
|
conv = gimple_build_assign (tem, NOP_EXPR, rhs1);
|
|
gsi_insert_before (gsi, conv, GSI_SAME_STMT);
|
|
gimple_assign_set_rhs1 (stmt, tem);
|
|
fold_stmt (gsi, follow_single_use_edges);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Simplify an internal fn call using ranges if possible. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify_internal_call_using_ranges
|
|
(gimple_stmt_iterator *gsi,
|
|
gimple *stmt)
|
|
{
|
|
enum tree_code subcode;
|
|
bool is_ubsan = false;
|
|
bool ovf = false;
|
|
switch (gimple_call_internal_fn (stmt))
|
|
{
|
|
case IFN_UBSAN_CHECK_ADD:
|
|
subcode = PLUS_EXPR;
|
|
is_ubsan = true;
|
|
break;
|
|
case IFN_UBSAN_CHECK_SUB:
|
|
subcode = MINUS_EXPR;
|
|
is_ubsan = true;
|
|
break;
|
|
case IFN_UBSAN_CHECK_MUL:
|
|
subcode = MULT_EXPR;
|
|
is_ubsan = true;
|
|
break;
|
|
case IFN_ADD_OVERFLOW:
|
|
subcode = PLUS_EXPR;
|
|
break;
|
|
case IFN_SUB_OVERFLOW:
|
|
subcode = MINUS_EXPR;
|
|
break;
|
|
case IFN_MUL_OVERFLOW:
|
|
subcode = MULT_EXPR;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
tree op0 = gimple_call_arg (stmt, 0);
|
|
tree op1 = gimple_call_arg (stmt, 1);
|
|
tree type;
|
|
if (is_ubsan)
|
|
{
|
|
type = TREE_TYPE (op0);
|
|
if (VECTOR_TYPE_P (type))
|
|
return false;
|
|
}
|
|
else if (gimple_call_lhs (stmt) == NULL_TREE)
|
|
return false;
|
|
else
|
|
type = TREE_TYPE (TREE_TYPE (gimple_call_lhs (stmt)));
|
|
if (!check_for_binary_op_overflow (query, subcode, type, op0, op1, &ovf, stmt)
|
|
|| (is_ubsan && ovf))
|
|
return false;
|
|
|
|
gimple *g;
|
|
location_t loc = gimple_location (stmt);
|
|
if (is_ubsan)
|
|
g = gimple_build_assign (gimple_call_lhs (stmt), subcode, op0, op1);
|
|
else
|
|
{
|
|
tree utype = type;
|
|
if (ovf
|
|
|| !useless_type_conversion_p (type, TREE_TYPE (op0))
|
|
|| !useless_type_conversion_p (type, TREE_TYPE (op1)))
|
|
utype = unsigned_type_for (type);
|
|
if (TREE_CODE (op0) == INTEGER_CST)
|
|
op0 = fold_convert (utype, op0);
|
|
else if (!useless_type_conversion_p (utype, TREE_TYPE (op0)))
|
|
{
|
|
g = gimple_build_assign (make_ssa_name (utype), NOP_EXPR, op0);
|
|
gimple_set_location (g, loc);
|
|
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
|
op0 = gimple_assign_lhs (g);
|
|
}
|
|
if (TREE_CODE (op1) == INTEGER_CST)
|
|
op1 = fold_convert (utype, op1);
|
|
else if (!useless_type_conversion_p (utype, TREE_TYPE (op1)))
|
|
{
|
|
g = gimple_build_assign (make_ssa_name (utype), NOP_EXPR, op1);
|
|
gimple_set_location (g, loc);
|
|
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
|
op1 = gimple_assign_lhs (g);
|
|
}
|
|
g = gimple_build_assign (make_ssa_name (utype), subcode, op0, op1);
|
|
gimple_set_location (g, loc);
|
|
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
|
if (utype != type)
|
|
{
|
|
g = gimple_build_assign (make_ssa_name (type), NOP_EXPR,
|
|
gimple_assign_lhs (g));
|
|
gimple_set_location (g, loc);
|
|
gsi_insert_before (gsi, g, GSI_SAME_STMT);
|
|
}
|
|
g = gimple_build_assign (gimple_call_lhs (stmt), COMPLEX_EXPR,
|
|
gimple_assign_lhs (g),
|
|
build_int_cst (type, ovf));
|
|
}
|
|
gimple_set_location (g, loc);
|
|
gsi_replace (gsi, g, false);
|
|
return true;
|
|
}
|
|
|
|
/* Return true if VAR is a two-valued variable. Set a and b with the
|
|
two-values when it is true. Return false otherwise. */
|
|
|
|
bool
|
|
simplify_using_ranges::two_valued_val_range_p (tree var, tree *a, tree *b,
|
|
gimple *s)
|
|
{
|
|
int_range_max vr;
|
|
if (!query->range_of_expr (vr, var, s))
|
|
return false;
|
|
if (vr.varying_p () || vr.undefined_p ())
|
|
return false;
|
|
|
|
if ((vr.num_pairs () == 1 && vr.upper_bound () - vr.lower_bound () == 1)
|
|
|| (vr.num_pairs () == 2
|
|
&& vr.lower_bound (0) == vr.upper_bound (0)
|
|
&& vr.lower_bound (1) == vr.upper_bound (1)))
|
|
{
|
|
*a = wide_int_to_tree (TREE_TYPE (var), vr.lower_bound ());
|
|
*b = wide_int_to_tree (TREE_TYPE (var), vr.upper_bound ());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
simplify_using_ranges::simplify_using_ranges (range_query *query,
|
|
int not_executable_flag)
|
|
: query (query)
|
|
{
|
|
to_remove_edges = vNULL;
|
|
to_update_switch_stmts = vNULL;
|
|
m_not_executable_flag = not_executable_flag;
|
|
m_flag_set_edges = vNULL;
|
|
}
|
|
|
|
simplify_using_ranges::~simplify_using_ranges ()
|
|
{
|
|
cleanup_edges_and_switches ();
|
|
m_flag_set_edges.release ();
|
|
}
|
|
|
|
/* Simplify STMT using ranges if possible. */
|
|
|
|
bool
|
|
simplify_using_ranges::simplify (gimple_stmt_iterator *gsi)
|
|
{
|
|
gcc_checking_assert (query);
|
|
|
|
gimple *stmt = gsi_stmt (*gsi);
|
|
if (is_gimple_assign (stmt))
|
|
{
|
|
enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
|
|
tree rhs1 = gimple_assign_rhs1 (stmt);
|
|
tree rhs2 = gimple_assign_rhs2 (stmt);
|
|
tree lhs = gimple_assign_lhs (stmt);
|
|
tree val1 = NULL_TREE, val2 = NULL_TREE;
|
|
use_operand_p use_p;
|
|
gimple *use_stmt;
|
|
|
|
/* Convert:
|
|
LHS = CST BINOP VAR
|
|
Where VAR is two-valued and LHS is used in GIMPLE_COND only
|
|
To:
|
|
LHS = VAR == VAL1 ? (CST BINOP VAL1) : (CST BINOP VAL2)
|
|
|
|
Also handles:
|
|
LHS = VAR BINOP CST
|
|
Where VAR is two-valued and LHS is used in GIMPLE_COND only
|
|
To:
|
|
LHS = VAR == VAL1 ? (VAL1 BINOP CST) : (VAL2 BINOP CST) */
|
|
|
|
if (TREE_CODE_CLASS (rhs_code) == tcc_binary
|
|
&& INTEGRAL_TYPE_P (TREE_TYPE (rhs1))
|
|
&& ((TREE_CODE (rhs1) == INTEGER_CST
|
|
&& TREE_CODE (rhs2) == SSA_NAME)
|
|
|| (TREE_CODE (rhs2) == INTEGER_CST
|
|
&& TREE_CODE (rhs1) == SSA_NAME))
|
|
&& single_imm_use (lhs, &use_p, &use_stmt)
|
|
&& gimple_code (use_stmt) == GIMPLE_COND)
|
|
|
|
{
|
|
tree new_rhs1 = NULL_TREE;
|
|
tree new_rhs2 = NULL_TREE;
|
|
tree cmp_var = NULL_TREE;
|
|
|
|
if (TREE_CODE (rhs2) == SSA_NAME
|
|
&& two_valued_val_range_p (rhs2, &val1, &val2, stmt))
|
|
{
|
|
/* Optimize RHS1 OP [VAL1, VAL2]. */
|
|
new_rhs1 = int_const_binop (rhs_code, rhs1, val1);
|
|
new_rhs2 = int_const_binop (rhs_code, rhs1, val2);
|
|
cmp_var = rhs2;
|
|
}
|
|
else if (TREE_CODE (rhs1) == SSA_NAME
|
|
&& two_valued_val_range_p (rhs1, &val1, &val2, stmt))
|
|
{
|
|
/* Optimize [VAL1, VAL2] OP RHS2. */
|
|
new_rhs1 = int_const_binop (rhs_code, val1, rhs2);
|
|
new_rhs2 = int_const_binop (rhs_code, val2, rhs2);
|
|
cmp_var = rhs1;
|
|
}
|
|
|
|
/* If we could not find two-vals or the optimzation is invalid as
|
|
in divide by zero, new_rhs1 / new_rhs will be NULL_TREE. */
|
|
if (new_rhs1 && new_rhs2)
|
|
{
|
|
tree cond = gimple_build (gsi, true, GSI_SAME_STMT,
|
|
UNKNOWN_LOCATION,
|
|
EQ_EXPR, boolean_type_node,
|
|
cmp_var, val1);
|
|
gimple_assign_set_rhs_with_ops (gsi,
|
|
COND_EXPR, cond,
|
|
new_rhs1,
|
|
new_rhs2);
|
|
update_stmt (gsi_stmt (*gsi));
|
|
fold_stmt (gsi, follow_single_use_edges);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (TREE_CODE_CLASS (rhs_code) == tcc_comparison)
|
|
return simplify_compare_assign_using_ranges_1 (gsi, stmt);
|
|
|
|
switch (rhs_code)
|
|
{
|
|
|
|
/* Transform TRUNC_DIV_EXPR and TRUNC_MOD_EXPR into RSHIFT_EXPR
|
|
and BIT_AND_EXPR respectively if the first operand is greater
|
|
than zero and the second operand is an exact power of two.
|
|
Also optimize TRUNC_MOD_EXPR away if the second operand is
|
|
constant and the first operand already has the right value
|
|
range. */
|
|
case TRUNC_DIV_EXPR:
|
|
case TRUNC_MOD_EXPR:
|
|
if ((TREE_CODE (rhs1) == SSA_NAME
|
|
|| TREE_CODE (rhs1) == INTEGER_CST)
|
|
&& INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
|
|
return simplify_div_or_mod_using_ranges (gsi, stmt);
|
|
break;
|
|
|
|
/* Transform ABS (X) into X or -X as appropriate. */
|
|
case ABS_EXPR:
|
|
if (TREE_CODE (rhs1) == SSA_NAME
|
|
&& INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
|
|
return simplify_abs_using_ranges (gsi, stmt);
|
|
break;
|
|
|
|
case BIT_AND_EXPR:
|
|
case BIT_IOR_EXPR:
|
|
/* Optimize away BIT_AND_EXPR and BIT_IOR_EXPR if all the bits
|
|
being cleared are already cleared or all the bits being set
|
|
are already set. Beware that boolean types must be handled
|
|
logically (see range-op.cc) unless they have precision 1. */
|
|
if (INTEGRAL_TYPE_P (TREE_TYPE (rhs1))
|
|
&& (TREE_CODE (TREE_TYPE (rhs1)) != BOOLEAN_TYPE
|
|
|| TYPE_PRECISION (TREE_TYPE (rhs1)) == 1))
|
|
return simplify_bit_ops_using_ranges (gsi, stmt);
|
|
break;
|
|
|
|
CASE_CONVERT:
|
|
if (TREE_CODE (rhs1) == SSA_NAME
|
|
&& INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
|
|
return simplify_conversion_using_ranges (gsi, stmt);
|
|
break;
|
|
|
|
case FLOAT_EXPR:
|
|
if (TREE_CODE (rhs1) == SSA_NAME
|
|
&& INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
|
|
return simplify_float_conversion_using_ranges (gsi, stmt);
|
|
break;
|
|
|
|
case MIN_EXPR:
|
|
case MAX_EXPR:
|
|
return simplify_min_or_max_using_ranges (gsi, stmt);
|
|
|
|
case RSHIFT_EXPR:
|
|
{
|
|
tree op0 = gimple_assign_rhs1 (stmt);
|
|
tree type = TREE_TYPE (op0);
|
|
int_range_max range;
|
|
if (TYPE_SIGN (type) == SIGNED
|
|
&& query->range_of_expr (range, op0, stmt))
|
|
{
|
|
unsigned prec = TYPE_PRECISION (TREE_TYPE (op0));
|
|
int_range<2> nzm1 (type, wi::minus_one (prec), wi::zero (prec),
|
|
VR_ANTI_RANGE);
|
|
range.intersect (nzm1);
|
|
// If there are no ranges other than [-1, 0] remove the shift.
|
|
if (range.undefined_p ())
|
|
{
|
|
gimple_assign_set_rhs_from_tree (gsi, op0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (gimple_code (stmt) == GIMPLE_COND)
|
|
return simplify_cond_using_ranges_1 (as_a <gcond *> (stmt));
|
|
else if (gimple_code (stmt) == GIMPLE_SWITCH)
|
|
return simplify_switch_using_ranges (as_a <gswitch *> (stmt));
|
|
else if (is_gimple_call (stmt)
|
|
&& gimple_call_internal_p (stmt))
|
|
return simplify_internal_call_using_ranges (gsi, stmt);
|
|
|
|
return false;
|
|
}
|