Commit a93cdc5c by Jakub Jelinek

re PR tree-optimization/77664 (Missed optimization: signed int >= 0 && < unsigned short)

	PR tree-optimization/77664
	* tree-ssa-reassoc.c (update_range_test): Also clear low and high
	for the other ranges.
	(optimize_range_tests_diff): Fix up formatting.
	(optimize_range_tests_var_bound): New function.
	(optimize_range_tests): Use it.

	* gcc.dg/tree-ssa/pr77664.c: New test.
	* gcc.dg/pr77664.c: New test.

From-SVN: r240858
parent 533144bc
2016-10-07 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/77664
* tree-ssa-reassoc.c (update_range_test): Also clear low and high
for the other ranges.
(optimize_range_tests_diff): Fix up formatting.
(optimize_range_tests_var_bound): New function.
(optimize_range_tests): Use it.
2016-10-07 Martin Liska <mliska@suse.cz> 2016-10-07 Martin Liska <mliska@suse.cz>
* coverage.c (build_gcov_exit_decl): Fix priority what * coverage.c (build_gcov_exit_decl): Fix priority what
......
2016-10-06 Louis Krupp <louis.krupp@zoho.com> 2016-10-07 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/77664
* gcc.dg/tree-ssa/pr77664.c: New test.
* gcc.dg/pr77664.c: New test.
2016-10-06 Louis Krupp <louis.krupp@zoho.com>
* gfortran.dg/pr69955.f90: New test. * gfortran.dg/pr69955.f90: New test.
......
/* PR tree-optimization/77664 */
/* { dg-do run } */
/* { dg-options "-O2" } */
#include "tree-ssa/pr77664.c"
int cnt;
__attribute__((noinline, noclone)) void
foo (void)
{
cnt++;
}
int
main ()
{
fn1 (65534U, 65535U, 7); if (cnt != 1) __builtin_abort ();
fn1 (65534U, 65535U, 0); if (cnt != 1) __builtin_abort ();
fn1 (65535U, 65535U, 1); if (cnt != 1) __builtin_abort ();
fn1 (0, 65535U, 1); if (cnt != 2) __builtin_abort ();
fn1 (-1, 65535U, 1); if (cnt != 2) __builtin_abort ();
fn1 (0, 0, 1); if (cnt != 2) __builtin_abort ();
fn2 (65534U, 65535U, 7); if (cnt != 3) __builtin_abort ();
fn2 (65534U, 65535U, 0); if (cnt != 3) __builtin_abort ();
fn2 (65535U, 65535U, 0); if (cnt != 4) __builtin_abort ();
fn2 (0, 65535U, 0); if (cnt != 4) __builtin_abort ();
fn2 (-1, 65535U, 0); if (cnt != 5) __builtin_abort ();
fn2 (0, 0, 0); if (cnt != 6) __builtin_abort ();
fn3 (-1, 65534U); if (cnt != 7) __builtin_abort ();
fn3 (0, 65534U); if (cnt != 7) __builtin_abort ();
fn3 (65534U, 65534U); if (cnt != 7) __builtin_abort ();
fn3 (65535U, 65534U); if (cnt != 8) __builtin_abort ();
fn3 (0, 0); if (cnt != 8) __builtin_abort ();
fn3 (1, 0); if (cnt != 9) __builtin_abort ();
fn4 (-1, 65534U); if (cnt != 9) __builtin_abort ();
fn4 (0, 65534U); if (cnt != 10) __builtin_abort ();
fn4 (65534U, 65534U); if (cnt != 11) __builtin_abort ();
fn4 (65535U, 65534U); if (cnt != 11) __builtin_abort ();
fn4 (0, 0); if (cnt != 12) __builtin_abort ();
fn4 (1, 0); if (cnt != 12) __builtin_abort ();
fn5 (-1, 65534U); if (cnt != 13) __builtin_abort ();
fn5 (0, 65534U); if (cnt != 13) __builtin_abort ();
fn5 (65534U, 65534U); if (cnt != 13) __builtin_abort ();
fn5 (65535U, 65534U); if (cnt != 14) __builtin_abort ();
fn5 (0, 0); if (cnt != 14) __builtin_abort ();
fn5 (1, 0); if (cnt != 15) __builtin_abort ();
fn6 (-1, 65534U); if (cnt != 15) __builtin_abort ();
fn6 (0, 65534U); if (cnt != 16) __builtin_abort ();
fn6 (65534U, 65534U); if (cnt != 17) __builtin_abort ();
fn6 (65535U, 65534U); if (cnt != 17) __builtin_abort ();
fn6 (0, 0); if (cnt != 18) __builtin_abort ();
fn6 (1, 0); if (cnt != 18) __builtin_abort ();
return 0;
}
/* PR tree-optimization/77664 */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-reassoc1-details" } */
extern void foo (void);
/* { dg-final { scan-tree-dump-times "Optimizing range test \[^\n\r]* and comparison" 6 "reassoc1" } } */
__attribute__((noinline, noclone)) void
fn1 (long long int a, unsigned short b, int c)
{
if (a >= 0 && c && a < b)
foo ();
}
__attribute__((noinline, noclone)) void
fn2 (long long int a, unsigned short b, int c)
{
if (a < 0 || c || a >= b)
foo ();
}
__attribute__((noinline, noclone)) void
fn3 (long long int a, unsigned short b)
{
if (a < 0 || b < a)
foo ();
}
__attribute__((noinline, noclone)) void
fn4 (long long int a, unsigned short b)
{
if (a <= b && a >= 0)
foo ();
}
__attribute__((noinline, noclone)) void
fn5 (long long int a, unsigned short b)
{
if (a < 0 | a > b)
foo ();
}
__attribute__((noinline, noclone)) void
fn6 (long long int a, unsigned short b)
{
if (b >= a & a >= 0)
foo ();
}
...@@ -2428,6 +2428,8 @@ update_range_test (struct range_entry *range, struct range_entry *otherrange, ...@@ -2428,6 +2428,8 @@ update_range_test (struct range_entry *range, struct range_entry *otherrange,
else else
oe->op = error_mark_node; oe->op = error_mark_node;
range->exp = NULL_TREE; range->exp = NULL_TREE;
range->low = NULL_TREE;
range->high = NULL_TREE;
} }
return true; return true;
} }
...@@ -2485,10 +2487,10 @@ optimize_range_tests_xor (enum tree_code opcode, tree type, ...@@ -2485,10 +2487,10 @@ optimize_range_tests_xor (enum tree_code opcode, tree type,
((X - 43U) & ~(75U - 43U)) <= 3U. */ ((X - 43U) & ~(75U - 43U)) <= 3U. */
static bool static bool
optimize_range_tests_diff (enum tree_code opcode, tree type, optimize_range_tests_diff (enum tree_code opcode, tree type,
tree lowi, tree lowj, tree highi, tree highj, tree lowi, tree lowj, tree highi, tree highj,
vec<operand_entry *> *ops, vec<operand_entry *> *ops,
struct range_entry *rangei, struct range_entry *rangei,
struct range_entry *rangej) struct range_entry *rangej)
{ {
tree tem1, tem2, mask; tree tem1, tem2, mask;
/* Check highi - lowi == highj - lowj. */ /* Check highi - lowi == highj - lowj. */
...@@ -2829,6 +2831,197 @@ optimize_range_tests_to_bit_test (enum tree_code opcode, int first, int length, ...@@ -2829,6 +2831,197 @@ optimize_range_tests_to_bit_test (enum tree_code opcode, int first, int length,
return any_changes; return any_changes;
} }
/* Attempt to optimize for signed a and b where b is known to be >= 0:
a >= 0 && a < b into (unsigned) a < (unsigned) b
a >= 0 && a <= b into (unsigned) a <= (unsigned) b */
static bool
optimize_range_tests_var_bound (enum tree_code opcode, int first, int length,
vec<operand_entry *> *ops,
struct range_entry *ranges)
{
int i;
bool any_changes = false;
hash_map<tree, int> *map = NULL;
for (i = first; i < length; i++)
{
if (ranges[i].exp == NULL_TREE || !ranges[i].in_p)
continue;
tree type = TREE_TYPE (ranges[i].exp);
if (!INTEGRAL_TYPE_P (type)
|| TYPE_UNSIGNED (type)
|| ranges[i].low == NULL_TREE
|| !integer_zerop (ranges[i].low)
|| ranges[i].high != NULL_TREE)
continue;
/* EXP >= 0 here. */
if (map == NULL)
map = new hash_map <tree, int>;
map->put (ranges[i].exp, i);
}
if (map == NULL)
return false;
for (i = 0; i < length; i++)
{
if (ranges[i].low == NULL_TREE
|| ranges[i].high == NULL_TREE
|| !integer_zerop (ranges[i].low)
|| !integer_zerop (ranges[i].high))
continue;
gimple *stmt;
tree_code ccode;
tree rhs1, rhs2;
if (ranges[i].exp)
{
stmt = SSA_NAME_DEF_STMT (ranges[i].exp);
if (!is_gimple_assign (stmt))
continue;
ccode = gimple_assign_rhs_code (stmt);
rhs1 = gimple_assign_rhs1 (stmt);
rhs2 = gimple_assign_rhs2 (stmt);
}
else
{
operand_entry *oe = (*ops)[ranges[i].idx];
stmt = last_stmt (BASIC_BLOCK_FOR_FN (cfun, oe->id));
if (gimple_code (stmt) != GIMPLE_COND)
continue;
ccode = gimple_cond_code (stmt);
rhs1 = gimple_cond_lhs (stmt);
rhs2 = gimple_cond_rhs (stmt);
}
if (TREE_CODE (rhs1) != SSA_NAME
|| rhs2 == NULL_TREE
|| TREE_CODE (rhs2) != SSA_NAME)
continue;
switch (ccode)
{
case GT_EXPR:
case GE_EXPR:
if (!ranges[i].in_p)
std::swap (rhs1, rhs2);
ccode = swap_tree_comparison (ccode);
break;
case LT_EXPR:
case LE_EXPR:
if (ranges[i].in_p)
std::swap (rhs1, rhs2);
break;
default:
continue;
}
int *idx = map->get (rhs1);
if (idx == NULL)
continue;
wide_int nz = get_nonzero_bits (rhs2);
if (wi::neg_p (nz))
continue;
/* We have EXP < RHS2 or EXP <= RHS2 where EXP >= 0
and RHS2 is known to be RHS2 >= 0. */
tree utype = unsigned_type_for (TREE_TYPE (rhs1));
enum warn_strict_overflow_code wc = WARN_STRICT_OVERFLOW_COMPARISON;
if ((ranges[*idx].strict_overflow_p
|| ranges[i].strict_overflow_p)
&& issue_strict_overflow_warning (wc))
warning_at (gimple_location (stmt), OPT_Wstrict_overflow,
"assuming signed overflow does not occur "
"when simplifying range test");
if (dump_file && (dump_flags & TDF_DETAILS))
{
struct range_entry *r = &ranges[*idx];
fprintf (dump_file, "Optimizing range test ");
print_generic_expr (dump_file, r->exp, 0);
fprintf (dump_file, " +[");
print_generic_expr (dump_file, r->low, 0);
fprintf (dump_file, ", ");
print_generic_expr (dump_file, r->high, 0);
fprintf (dump_file, "] and comparison ");
print_generic_expr (dump_file, rhs1, 0);
fprintf (dump_file, " %s ", op_symbol_code (ccode));
print_generic_expr (dump_file, rhs2, 0);
fprintf (dump_file, "\n into (");
print_generic_expr (dump_file, utype, 0);
fprintf (dump_file, ") ");
print_generic_expr (dump_file, rhs1, 0);
fprintf (dump_file, " %s (", op_symbol_code (ccode));
print_generic_expr (dump_file, utype, 0);
fprintf (dump_file, ") ");
print_generic_expr (dump_file, rhs2, 0);
fprintf (dump_file, "\n");
}
if (ranges[i].in_p)
std::swap (rhs1, rhs2);
unsigned int uid = gimple_uid (stmt);
gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
gimple *g = gimple_build_assign (make_ssa_name (utype), NOP_EXPR, rhs1);
gimple_set_uid (g, uid);
rhs1 = gimple_assign_lhs (g);
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
g = gimple_build_assign (make_ssa_name (utype), NOP_EXPR, rhs2);
gimple_set_uid (g, uid);
rhs2 = gimple_assign_lhs (g);
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
if (tree_swap_operands_p (rhs1, rhs2, false))
{
std::swap (rhs1, rhs2);
ccode = swap_tree_comparison (ccode);
}
if (gimple_code (stmt) == GIMPLE_COND)
{
gcond *c = as_a <gcond *> (stmt);
gimple_cond_set_code (c, ccode);
gimple_cond_set_lhs (c, rhs1);
gimple_cond_set_rhs (c, rhs2);
update_stmt (stmt);
}
else
{
g = gimple_build_assign (make_ssa_name (TREE_TYPE (ranges[i].exp)),
ccode, rhs1, rhs2);
gimple_set_uid (g, uid);
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
ranges[i].exp = gimple_assign_lhs (g);
(*ops)[ranges[i].idx]->op = ranges[i].exp;
}
ranges[i].strict_overflow_p = false;
operand_entry *oe = (*ops)[ranges[*idx].idx];
/* Now change all the other range test immediate uses, so that
those tests will be optimized away. */
if (opcode == ERROR_MARK)
{
if (oe->op)
oe->op = build_int_cst (TREE_TYPE (oe->op),
oe->rank == BIT_IOR_EXPR ? 0 : 1);
else
oe->op = (oe->rank == BIT_IOR_EXPR
? boolean_false_node : boolean_true_node);
}
else
oe->op = error_mark_node;
ranges[*idx].exp = NULL_TREE;
ranges[*idx].low = NULL_TREE;
ranges[*idx].high = NULL_TREE;
any_changes = true;
}
delete map;
return any_changes;
}
/* Optimize range tests, similarly how fold_range_test optimizes /* Optimize range tests, similarly how fold_range_test optimizes
it on trees. The tree code for the binary it on trees. The tree code for the binary
operation between all the operands is OPCODE. operation between all the operands is OPCODE.
...@@ -2917,6 +3110,8 @@ optimize_range_tests (enum tree_code opcode, ...@@ -2917,6 +3110,8 @@ optimize_range_tests (enum tree_code opcode,
if (lshift_cheap_p (optimize_function_for_speed_p (cfun))) if (lshift_cheap_p (optimize_function_for_speed_p (cfun)))
any_changes |= optimize_range_tests_to_bit_test (opcode, first, length, any_changes |= optimize_range_tests_to_bit_test (opcode, first, length,
ops, ranges); ops, ranges);
any_changes |= optimize_range_tests_var_bound (opcode, first, length, ops,
ranges);
if (any_changes && opcode != ERROR_MARK) if (any_changes && opcode != ERROR_MARK)
{ {
......
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