Commit 68e57f04 by Richard Sandiford Committed by Richard Sandiford

Make tree_expr_nonnegative_warnv_p recurse into SSA names

The upcoming patch to move sqrt and cbrt simplifications to match.pd
caused a regression because the (abs @0)->@0 simplification didn't
trigger for:

        (abs (convert (abs X)))

The simplification is based on tree_expr_nonnegative_p, which at
the moment just gives up if it sees an SSA_NAME.

This patch makes tree_expr_nonnegative_p recurse into SSA name
definitions, but limits the depth of recursion to a small number
for the reason mentioned in the comment (adapted from an existing
comment in gimple_val_nonnegative_real_p).  The patch reuses code
in tree-vrp.c, moving it to gimple-fold.c.  It also replaces calls
to gimple_val_nonnegative_real_p with calls to tree_expr_nonnegative_p.

A knock-on effect is that we can now prove _i_589 < 0 is false in
sequences like:

      i_1917 = ASSERT_EXPR <i_1075, i_1075 == 0>;
      _i_589 = (const int) i_1917;
      _i_1507 = ASSERT_EXPR <_i_589, _i_589 < 0>;

This defeats an assert in tree-vrp.c that ASSERT_EXPR conditions
are never known to be false.  Previously the assert only ever used
local knowledge and so would be limited to cases like x != x for
integer x.  Now that we use global knowledge it's possible to prove
the assertion condition is false in blocks that are in practice
unreachable.  The patch therefore removes the assert.

Bootstrapped & regression-tested on x86_64-linux-gnu.  I didn't write
a specific test because this is already covered by the testsuite if
the follow-on patch is also applied.

gcc/
	* params.def (PARAM_MAX_SSA_NAME_QUERY_DEPTH): New param.
	* doc/invoke.texi (--param max-ssa-name-query-depth): Document.
	* fold-const.h (tree_unary_nonnegative_warnv_p)
	(tree_single_nonnegative_warnv_p, tree_call_nonnegative_warnv_p)
	(tree_expr_nonnegative_warnv_p): Add depth parameters.
	* fold-const.c: Include gimple-fold.h and params.h.
	(tree_ssa_name_nonnegative_warnv_p): New function.
	(tree_unary_nonnegative_warnv_p, tree_binary_nonnegative_warnv_p)
	(tree_single_nonnegative_warnv_p, tree_call_nonnegative_warnv_p)
	(tree_invalid_nonnegative_warnv_p, tree_expr_nonnegative_warnv_p):
	Add a depth parameter and increment it for recursive calls to
	tree_expr_nonnegative_warnv_p.  Use tree_ssa_name_nonnegative_warnv_p
	to handle SSA names.
	* gimple-fold.h (gimple_val_nonnegative_real_p): Delete.
	(gimple_stmt_nonnegative_warnv_p): Declare.
	* tree-vrp.c (remove_range_assertions): Remove assert that condition
	cannot be proven false.
	(gimple_assign_nonnegative_warnv_p, gimple_call_nonnegative_warnv_p)
	(gimple_stmt_nonnegative_warnv_p): Move to...
	* gimple-fold.c: ...here.  Add depth parameters and pass them
	down to the tree routines.  Accept statements that aren't
	assignments or calls but just return false for them.
	(gimple_val_nonnegative_real_p): Delete.
	* tree-ssa-math-opts.c (gimple_expand_builtin_pow): Use
	tree_expr_nonnegative_p instead of gimple_val_nonnegative_real_p.
	Check HONOR_NANs first.

From-SVN: r228614
parent 170f473b
2015-10-08 Richard Sandiford <richard.sandiford@arm.com>
* params.def (PARAM_MAX_SSA_NAME_QUERY_DEPTH): New param.
* doc/invoke.texi (--param max-ssa-name-query-depth): Document.
* fold-const.h (tree_unary_nonnegative_warnv_p)
(tree_single_nonnegative_warnv_p, tree_call_nonnegative_warnv_p)
(tree_expr_nonnegative_warnv_p): Add depth parameters.
* fold-const.c: Include gimple-fold.h and params.h.
(tree_ssa_name_nonnegative_warnv_p): New function.
(tree_unary_nonnegative_warnv_p, tree_binary_nonnegative_warnv_p)
(tree_single_nonnegative_warnv_p, tree_call_nonnegative_warnv_p)
(tree_invalid_nonnegative_warnv_p, tree_expr_nonnegative_warnv_p):
Add a depth parameter and increment it for recursive calls to
tree_expr_nonnegative_warnv_p. Use tree_ssa_name_nonnegative_warnv_p
to handle SSA names.
* gimple-fold.h (gimple_val_nonnegative_real_p): Delete.
(gimple_stmt_nonnegative_warnv_p): Declare.
* tree-vrp.c (remove_range_assertions): Remove assert that condition
cannot be proven false.
(gimple_assign_nonnegative_warnv_p, gimple_call_nonnegative_warnv_p)
(gimple_stmt_nonnegative_warnv_p): Move to...
* gimple-fold.c: ...here. Add depth parameters and pass them
down to the tree routines. Accept statements that aren't
assignments or calls but just return false for them.
(gimple_val_nonnegative_real_p): Delete.
* tree-ssa-math-opts.c (gimple_expand_builtin_pow): Use
tree_expr_nonnegative_p instead of gimple_val_nonnegative_real_p.
Check HONOR_NANs first.
2015-10-08 Martin Jambor <mjambor@suse.cz>
* ipa-cp.c (meet_with_1): Make the argument of abs signed. Remove
......@@ -11132,6 +11132,10 @@ automaton. The default is 50.
Chunk size of omp schedule for loops parallelized by parloops. The default
is 0.
@item max-ssa-name-query-depth
Maximum depth of recursion when querying properties of SSA names in things
like fold routines. One level of recursion corresponds to following a
use-def chain.
@end table
@end table
......
......@@ -132,11 +132,13 @@ extern bool tree_unary_nonzero_warnv_p (enum tree_code, tree, tree, bool *);
extern bool tree_binary_nonzero_warnv_p (enum tree_code, tree, tree, tree op1,
bool *);
extern bool tree_single_nonzero_warnv_p (tree, bool *);
extern bool tree_unary_nonnegative_warnv_p (enum tree_code, tree, tree, bool *);
extern bool tree_unary_nonnegative_warnv_p (enum tree_code, tree, tree,
bool *, int);
extern bool tree_binary_nonnegative_warnv_p (enum tree_code, tree, tree, tree,
bool *);
extern bool tree_single_nonnegative_warnv_p (tree t, bool *strict_overflow_p);
extern bool tree_call_nonnegative_warnv_p (tree, tree, tree, tree, bool *);
bool *, int);
extern bool tree_single_nonnegative_warnv_p (tree, bool *, int);
extern bool tree_call_nonnegative_warnv_p (tree, tree, tree, tree, bool *,
int);
extern bool fold_real_zero_addition_p (const_tree, const_tree, int);
extern tree combine_comparisons (location_t, enum tree_code, enum tree_code,
......@@ -160,7 +162,7 @@ extern tree size_diffop_loc (location_t, tree, tree);
extern tree non_lvalue_loc (location_t, tree);
extern bool tree_expr_nonnegative_p (tree);
extern bool tree_expr_nonnegative_warnv_p (tree, bool *);
extern bool tree_expr_nonnegative_warnv_p (tree, bool *, int = 0);
extern tree make_range (tree, int *, tree *, tree *, bool *);
extern tree make_range_step (location_t, enum tree_code, tree, tree, tree,
tree *, tree *, int *, bool *);
......
......@@ -5773,137 +5773,6 @@ gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo,
return gimple_get_virt_method_for_vtable (token, v, offset, can_refer);
}
/* Return true iff VAL is a gimple expression that is known to be
non-negative. Restricted to floating-point inputs. */
bool
gimple_val_nonnegative_real_p (tree val)
{
gimple *def_stmt;
gcc_assert (val && SCALAR_FLOAT_TYPE_P (TREE_TYPE (val)));
/* Use existing logic for non-gimple trees. */
if (tree_expr_nonnegative_p (val))
return true;
if (TREE_CODE (val) != SSA_NAME)
return false;
/* Currently we look only at the immediately defining statement
to make this determination, since recursion on defining
statements of operands can lead to quadratic behavior in the
worst case. This is expected to catch almost all occurrences
in practice. It would be possible to implement limited-depth
recursion if important cases are lost. Alternatively, passes
that need this information (such as the pow/powi lowering code
in the cse_sincos pass) could be revised to provide it through
dataflow propagation. */
def_stmt = SSA_NAME_DEF_STMT (val);
if (is_gimple_assign (def_stmt))
{
tree op0, op1;
/* See fold-const.c:tree_expr_nonnegative_p for additional
cases that could be handled with recursion. */
switch (gimple_assign_rhs_code (def_stmt))
{
case ABS_EXPR:
/* Always true for floating-point operands. */
return true;
case MULT_EXPR:
/* True if the two operands are identical (since we are
restricted to floating-point inputs). */
op0 = gimple_assign_rhs1 (def_stmt);
op1 = gimple_assign_rhs2 (def_stmt);
if (op0 == op1
|| operand_equal_p (op0, op1, 0))
return true;
default:
return false;
}
}
else if (is_gimple_call (def_stmt))
{
tree fndecl = gimple_call_fndecl (def_stmt);
if (fndecl
&& DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
{
tree arg1;
switch (DECL_FUNCTION_CODE (fndecl))
{
CASE_FLT_FN (BUILT_IN_ACOS):
CASE_FLT_FN (BUILT_IN_ACOSH):
CASE_FLT_FN (BUILT_IN_CABS):
CASE_FLT_FN (BUILT_IN_COSH):
CASE_FLT_FN (BUILT_IN_ERFC):
CASE_FLT_FN (BUILT_IN_EXP):
CASE_FLT_FN (BUILT_IN_EXP10):
CASE_FLT_FN (BUILT_IN_EXP2):
CASE_FLT_FN (BUILT_IN_FABS):
CASE_FLT_FN (BUILT_IN_FDIM):
CASE_FLT_FN (BUILT_IN_HYPOT):
CASE_FLT_FN (BUILT_IN_POW10):
return true;
CASE_FLT_FN (BUILT_IN_SQRT):
/* sqrt(-0.0) is -0.0, and sqrt is not defined over other
nonnegative inputs. */
if (!HONOR_SIGNED_ZEROS (val))
return true;
break;
CASE_FLT_FN (BUILT_IN_POWI):
/* True if the second argument is an even integer. */
arg1 = gimple_call_arg (def_stmt, 1);
if (TREE_CODE (arg1) == INTEGER_CST
&& (TREE_INT_CST_LOW (arg1) & 1) == 0)
return true;
break;
CASE_FLT_FN (BUILT_IN_POW):
/* True if the second argument is an even integer-valued
real. */
arg1 = gimple_call_arg (def_stmt, 1);
if (TREE_CODE (arg1) == REAL_CST)
{
REAL_VALUE_TYPE c;
HOST_WIDE_INT n;
c = TREE_REAL_CST (arg1);
n = real_to_integer (&c);
if ((n & 1) == 0)
{
REAL_VALUE_TYPE cint;
real_from_integer (&cint, VOIDmode, n, SIGNED);
if (real_identical (&c, &cint))
return true;
}
}
break;
default:
return false;
}
}
}
return false;
}
/* Given a pointer value OP0, return a simplified version of an
indirection through OP0, or NULL_TREE if no simplification is
possible. Note that the resulting type may be different from
......@@ -6280,3 +6149,80 @@ gimple_convert (gimple_seq *seq, location_t loc, tree type, tree op)
return op;
return gimple_build (seq, loc, NOP_EXPR, type, op);
}
/* Return true if the result of assignment STMT is known to be non-negative.
If the return value is based on the assumption that signed overflow is
undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
*STRICT_OVERFLOW_P. DEPTH is the current nesting depth of the query. */
static bool
gimple_assign_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
int depth)
{
enum tree_code code = gimple_assign_rhs_code (stmt);
switch (get_gimple_rhs_class (code))
{
case GIMPLE_UNARY_RHS:
return tree_unary_nonnegative_warnv_p (gimple_assign_rhs_code (stmt),
gimple_expr_type (stmt),
gimple_assign_rhs1 (stmt),
strict_overflow_p, depth);
case GIMPLE_BINARY_RHS:
return tree_binary_nonnegative_warnv_p (gimple_assign_rhs_code (stmt),
gimple_expr_type (stmt),
gimple_assign_rhs1 (stmt),
gimple_assign_rhs2 (stmt),
strict_overflow_p, depth);
case GIMPLE_TERNARY_RHS:
return false;
case GIMPLE_SINGLE_RHS:
return tree_single_nonnegative_warnv_p (gimple_assign_rhs1 (stmt),
strict_overflow_p, depth);
case GIMPLE_INVALID_RHS:
break;
}
gcc_unreachable ();
}
/* Return true if return value of call STMT is known to be non-negative.
If the return value is based on the assumption that signed overflow is
undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
*STRICT_OVERFLOW_P. DEPTH is the current nesting depth of the query. */
static bool
gimple_call_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
int depth)
{
tree arg0 = gimple_call_num_args (stmt) > 0 ?
gimple_call_arg (stmt, 0) : NULL_TREE;
tree arg1 = gimple_call_num_args (stmt) > 1 ?
gimple_call_arg (stmt, 1) : NULL_TREE;
return tree_call_nonnegative_warnv_p (gimple_expr_type (stmt),
gimple_call_fndecl (stmt),
arg0,
arg1,
strict_overflow_p, depth);
}
/* Return true if STMT is known to compute a non-negative value.
If the return value is based on the assumption that signed overflow is
undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
*STRICT_OVERFLOW_P. DEPTH is the current nesting depth of the query. */
bool
gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
int depth)
{
switch (gimple_code (stmt))
{
case GIMPLE_ASSIGN:
return gimple_assign_nonnegative_warnv_p (stmt, strict_overflow_p,
depth);
case GIMPLE_CALL:
return gimple_call_nonnegative_warnv_p (stmt, strict_overflow_p,
depth);
default:
return false;
}
}
......@@ -48,7 +48,6 @@ extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree,
extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, tree,
unsigned HOST_WIDE_INT,
bool *can_refer = NULL);
extern bool gimple_val_nonnegative_real_p (tree);
extern tree gimple_fold_indirect_ref (tree);
extern bool arith_code_with_undefined_signed_overflow (tree_code);
extern gimple_seq rewrite_to_defined_overflow (gimple *);
......@@ -113,6 +112,8 @@ gimple_convert (gimple_seq *seq, tree type, tree op)
return gimple_convert (seq, UNKNOWN_LOCATION, type, op);
}
extern bool gimple_stmt_nonnegative_warnv_p (gimple *, bool *, int = 0);
/* In gimple-match.c. */
extern tree gimple_simplify (enum tree_code, tree, tree,
gimple_seq *, tree (*)(tree));
......
......@@ -1152,6 +1152,12 @@ DEFPARAM (PARAM_PARLOOPS_CHUNK_SIZE,
"parloops-chunk-size",
"Chunk size of omp schedule for loops parallelized by parloops",
0, 0, 0)
DEFPARAM (PARAM_MAX_SSA_NAME_QUERY_DEPTH,
"max-ssa-name-query-depth",
"Maximum recursion depth allowed when querying a property of an"
" SSA name",
2, 1, 0)
/*
Local variables:
......
......@@ -1526,7 +1526,7 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
if (flag_unsafe_math_optimizations
&& cbrtfn
&& (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode))
&& (!HONOR_NANS (mode) || tree_expr_nonnegative_p (arg0))
&& real_equal (&c, &dconst1_3))
return build_and_insert_call (gsi, loc, cbrtfn, arg0);
......@@ -1538,7 +1538,7 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
if (flag_unsafe_math_optimizations
&& sqrtfn
&& cbrtfn
&& (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode))
&& (!HONOR_NANS (mode) || tree_expr_nonnegative_p (arg0))
&& speed_p
&& hw_sqrt_exists
&& real_equal (&c, &dconst1_6))
......@@ -1594,7 +1594,7 @@ gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc,
if (flag_unsafe_math_optimizations
&& cbrtfn
&& (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode))
&& (!HONOR_NANS (mode) || tree_expr_nonnegative_p (arg0))
&& real_identical (&c2, &c)
&& !c2_is_int
&& optimize_function_for_speed_p (cfun)
......
......@@ -1007,80 +1007,6 @@ usable_range_p (value_range *vr, bool *strict_overflow_p)
return true;
}
/* Return true if the result of assignment STMT is know to be non-negative.
If the return value is based on the assumption that signed overflow is
undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
*STRICT_OVERFLOW_P.*/
static bool
gimple_assign_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p)
{
enum tree_code code = gimple_assign_rhs_code (stmt);
switch (get_gimple_rhs_class (code))
{
case GIMPLE_UNARY_RHS:
return tree_unary_nonnegative_warnv_p (gimple_assign_rhs_code (stmt),
gimple_expr_type (stmt),
gimple_assign_rhs1 (stmt),
strict_overflow_p);
case GIMPLE_BINARY_RHS:
return tree_binary_nonnegative_warnv_p (gimple_assign_rhs_code (stmt),
gimple_expr_type (stmt),
gimple_assign_rhs1 (stmt),
gimple_assign_rhs2 (stmt),
strict_overflow_p);
case GIMPLE_TERNARY_RHS:
return false;
case GIMPLE_SINGLE_RHS:
return tree_single_nonnegative_warnv_p (gimple_assign_rhs1 (stmt),
strict_overflow_p);
case GIMPLE_INVALID_RHS:
gcc_unreachable ();
default:
gcc_unreachable ();
}
}
/* Return true if return value of call STMT is know to be non-negative.
If the return value is based on the assumption that signed overflow is
undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
*STRICT_OVERFLOW_P.*/
static bool
gimple_call_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p)
{
tree arg0 = gimple_call_num_args (stmt) > 0 ?
gimple_call_arg (stmt, 0) : NULL_TREE;
tree arg1 = gimple_call_num_args (stmt) > 1 ?
gimple_call_arg (stmt, 1) : NULL_TREE;
return tree_call_nonnegative_warnv_p (gimple_expr_type (stmt),
gimple_call_fndecl (stmt),
arg0,
arg1,
strict_overflow_p);
}
/* Return true if STMT is know to compute a non-negative value.
If the return value is based on the assumption that signed overflow is
undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
*STRICT_OVERFLOW_P.*/
static bool
gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p)
{
switch (gimple_code (stmt))
{
case GIMPLE_ASSIGN:
return gimple_assign_nonnegative_warnv_p (stmt, strict_overflow_p);
case GIMPLE_CALL:
return gimple_call_nonnegative_warnv_p (stmt, strict_overflow_p);
default:
gcc_unreachable ();
}
}
/* Return true if the result of assignment STMT is know to be non-zero.
If the return value is based on the assumption that signed overflow is
undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
......@@ -6858,12 +6784,9 @@ remove_range_assertions (void)
tree lhs = gimple_assign_lhs (stmt);
tree rhs = gimple_assign_rhs1 (stmt);
tree var;
tree cond = fold (ASSERT_EXPR_COND (rhs));
use_operand_p use_p;
imm_use_iterator iter;
gcc_assert (cond != boolean_false_node);
var = ASSERT_EXPR_VAR (rhs);
gcc_assert (TREE_CODE (var) == SSA_NAME);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment