Commit 0ccb5dbf by Jakub Jelinek Committed by Jakub Jelinek

re PR tree-optimization/46309 (optimization a==3||a==1)

	PR tree-optimization/46309
	* fold-const.c (make_range, merge_ranges): Remove prototypes.
	(make_range_step): New function.
	(make_range): Use it.
	* tree.h (make_range_step): New prototypes.
	* Makefile.in (tree-ssa-reassoc.o): Depend on $(DIAGNOSTIC_CORE_H).
	* tree-ssa-reassoc.c: Include diagnostic-core.h.
	(struct range_entry): New type.
	(init_range_entry, range_entry_cmp, update_range_test,
	optimize_range_tests): New functions.
	(reassociate_bb): Call optimize_range_tests.

	* gcc.dg/pr46309.c: New test.

From-SVN: r179388
parent 915afed6
2011-09-30 Jakub Jelinek <jakub@redhat.com> 2011-09-30 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/46309
* fold-const.c (make_range, merge_ranges): Remove prototypes.
(make_range_step): New function.
(make_range): Use it.
* tree.h (make_range_step): New prototypes.
* Makefile.in (tree-ssa-reassoc.o): Depend on $(DIAGNOSTIC_CORE_H).
* tree-ssa-reassoc.c: Include diagnostic-core.h.
(struct range_entry): New type.
(init_range_entry, range_entry_cmp, update_range_test,
optimize_range_tests): New functions.
(reassociate_bb): Call optimize_range_tests.
2011-09-30 Jakub Jelinek <jakub@redhat.com>
Richard Guenther <rguenther@suse.de> Richard Guenther <rguenther@suse.de>
* tree-ssa-structalias.c (find_func_aliases_for_builtin_call): Handle * tree-ssa-structalias.c (find_func_aliases_for_builtin_call): Handle
...@@ -2650,7 +2650,7 @@ tree-ssa-reassoc.o : tree-ssa-reassoc.c $(TREE_FLOW_H) $(CONFIG_H) \ ...@@ -2650,7 +2650,7 @@ tree-ssa-reassoc.o : tree-ssa-reassoc.c $(TREE_FLOW_H) $(CONFIG_H) \
$(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_PASS_H) $(FLAGS_H) \ $(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_PASS_H) $(FLAGS_H) \
tree-iterator.h $(BASIC_BLOCK_H) $(GIMPLE_H) $(TREE_INLINE_H) \ tree-iterator.h $(BASIC_BLOCK_H) $(GIMPLE_H) $(TREE_INLINE_H) \
$(VEC_H) langhooks.h alloc-pool.h pointer-set.h $(CFGLOOP_H) \ $(VEC_H) langhooks.h alloc-pool.h pointer-set.h $(CFGLOOP_H) \
tree-pretty-print.h gimple-pretty-print.h tree-pretty-print.h gimple-pretty-print.h $(DIAGNOSTIC_CORE_H)
tree-optimize.o : tree-optimize.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ tree-optimize.o : tree-optimize.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
$(TREE_H) $(TM_P_H) $(GGC_H) output.h \ $(TREE_H) $(TM_P_H) $(GGC_H) output.h \
$(DIAGNOSTIC_H) $(BASIC_BLOCK_H) $(FLAGS_H) $(TIMEVAR_H) $(TM_H) \ $(DIAGNOSTIC_H) $(BASIC_BLOCK_H) $(FLAGS_H) $(TIMEVAR_H) $(TM_H) \
......
...@@ -115,9 +115,6 @@ static int simple_operand_p (const_tree); ...@@ -115,9 +115,6 @@ static int simple_operand_p (const_tree);
static tree range_binop (enum tree_code, tree, tree, int, tree, int); static tree range_binop (enum tree_code, tree, tree, int, tree, int);
static tree range_predecessor (tree); static tree range_predecessor (tree);
static tree range_successor (tree); static tree range_successor (tree);
extern tree make_range (tree, int *, tree *, tree *, bool *);
extern bool merge_ranges (int *, tree *, tree *, int, tree, tree, int,
tree, tree);
static tree fold_range_test (location_t, enum tree_code, tree, tree, tree); static tree fold_range_test (location_t, enum tree_code, tree, tree, tree);
static tree fold_cond_expr_with_comparison (location_t, tree, tree, tree, tree); static tree fold_cond_expr_with_comparison (location_t, tree, tree, tree, tree);
static tree unextend (tree, int, int, tree); static tree unextend (tree, int, int, tree);
...@@ -3790,288 +3787,308 @@ range_binop (enum tree_code code, tree type, tree arg0, int upper0_p, ...@@ -3790,288 +3787,308 @@ range_binop (enum tree_code code, tree type, tree arg0, int upper0_p,
return constant_boolean_node (result, type); return constant_boolean_node (result, type);
} }
/* Given EXP, a logical expression, set the range it is testing into /* Helper routine for make_range. Perform one step for it, return
variables denoted by PIN_P, PLOW, and PHIGH. Return the expression new expression if the loop should continue or NULL_TREE if it should
actually being tested. *PLOW and *PHIGH will be made of the same stop. */
type as the returned expression. If EXP is not a comparison, we
will most likely not be returning a useful value and range. Set
*STRICT_OVERFLOW_P to true if the return value is only valid
because signed overflow is undefined; otherwise, do not change
*STRICT_OVERFLOW_P. */
tree tree
make_range (tree exp, int *pin_p, tree *plow, tree *phigh, make_range_step (location_t loc, enum tree_code code, tree arg0, tree arg1,
bool *strict_overflow_p) tree exp_type, tree *p_low, tree *p_high, int *p_in_p,
bool *strict_overflow_p)
{ {
enum tree_code code; tree arg0_type = TREE_TYPE (arg0);
tree arg0 = NULL_TREE, arg1 = NULL_TREE; tree n_low, n_high, low = *p_low, high = *p_high;
tree exp_type = NULL_TREE, arg0_type = NULL_TREE; int in_p = *p_in_p, n_in_p;
int in_p, n_in_p;
tree low, high, n_low, n_high;
location_t loc = EXPR_LOCATION (exp);
/* Start with simply saying "EXP != 0" and then look at the code of EXP
and see if we can refine the range. Some of the cases below may not
happen, but it doesn't seem worth worrying about this. We "continue"
the outer loop when we've changed something; otherwise we "break"
the switch, which will "break" the while. */
in_p = 0;
low = high = build_int_cst (TREE_TYPE (exp), 0);
while (1) switch (code)
{ {
code = TREE_CODE (exp); case TRUTH_NOT_EXPR:
exp_type = TREE_TYPE (exp); *p_in_p = ! in_p;
return arg0;
case EQ_EXPR: case NE_EXPR:
case LT_EXPR: case LE_EXPR: case GE_EXPR: case GT_EXPR:
/* We can only do something if the range is testing for zero
and if the second operand is an integer constant. Note that
saying something is "in" the range we make is done by
complementing IN_P since it will set in the initial case of
being not equal to zero; "out" is leaving it alone. */
if (low == NULL_TREE || high == NULL_TREE
|| ! integer_zerop (low) || ! integer_zerop (high)
|| TREE_CODE (arg1) != INTEGER_CST)
return NULL_TREE;
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))) switch (code)
{ {
if (TREE_OPERAND_LENGTH (exp) > 0) case NE_EXPR: /* - [c, c] */
arg0 = TREE_OPERAND (exp, 0); low = high = arg1;
if (TREE_CODE_CLASS (code) == tcc_comparison break;
|| TREE_CODE_CLASS (code) == tcc_unary case EQ_EXPR: /* + [c, c] */
|| TREE_CODE_CLASS (code) == tcc_binary) in_p = ! in_p, low = high = arg1;
arg0_type = TREE_TYPE (arg0); break;
if (TREE_CODE_CLASS (code) == tcc_binary case GT_EXPR: /* - [-, c] */
|| TREE_CODE_CLASS (code) == tcc_comparison low = 0, high = arg1;
|| (TREE_CODE_CLASS (code) == tcc_expression break;
&& TREE_OPERAND_LENGTH (exp) > 1)) case GE_EXPR: /* + [c, -] */
arg1 = TREE_OPERAND (exp, 1); in_p = ! in_p, low = arg1, high = 0;
break;
case LT_EXPR: /* - [c, -] */
low = arg1, high = 0;
break;
case LE_EXPR: /* + [-, c] */
in_p = ! in_p, low = 0, high = arg1;
break;
default:
gcc_unreachable ();
} }
switch (code) /* If this is an unsigned comparison, we also know that EXP is
greater than or equal to zero. We base the range tests we make
on that fact, so we record it here so we can parse existing
range tests. We test arg0_type since often the return type
of, e.g. EQ_EXPR, is boolean. */
if (TYPE_UNSIGNED (arg0_type) && (low == 0 || high == 0))
{ {
case TRUTH_NOT_EXPR: if (! merge_ranges (&n_in_p, &n_low, &n_high,
in_p = ! in_p, exp = arg0; in_p, low, high, 1,
continue; build_int_cst (arg0_type, 0),
NULL_TREE))
case EQ_EXPR: case NE_EXPR: return NULL_TREE;
case LT_EXPR: case LE_EXPR: case GE_EXPR: case GT_EXPR:
/* We can only do something if the range is testing for zero
and if the second operand is an integer constant. Note that
saying something is "in" the range we make is done by
complementing IN_P since it will set in the initial case of
being not equal to zero; "out" is leaving it alone. */
if (low == 0 || high == 0
|| ! integer_zerop (low) || ! integer_zerop (high)
|| TREE_CODE (arg1) != INTEGER_CST)
break;
switch (code) in_p = n_in_p, low = n_low, high = n_high;
{
case NE_EXPR: /* - [c, c] */
low = high = arg1;
break;
case EQ_EXPR: /* + [c, c] */
in_p = ! in_p, low = high = arg1;
break;
case GT_EXPR: /* - [-, c] */
low = 0, high = arg1;
break;
case GE_EXPR: /* + [c, -] */
in_p = ! in_p, low = arg1, high = 0;
break;
case LT_EXPR: /* - [c, -] */
low = arg1, high = 0;
break;
case LE_EXPR: /* + [-, c] */
in_p = ! in_p, low = 0, high = arg1;
break;
default:
gcc_unreachable ();
}
/* If this is an unsigned comparison, we also know that EXP is /* If the high bound is missing, but we have a nonzero low
greater than or equal to zero. We base the range tests we make bound, reverse the range so it goes from zero to the low bound
on that fact, so we record it here so we can parse existing minus 1. */
range tests. We test arg0_type since often the return type if (high == 0 && low && ! integer_zerop (low))
of, e.g. EQ_EXPR, is boolean. */
if (TYPE_UNSIGNED (arg0_type) && (low == 0 || high == 0))
{ {
if (! merge_ranges (&n_in_p, &n_low, &n_high, in_p = ! in_p;
in_p, low, high, 1, high = range_binop (MINUS_EXPR, NULL_TREE, low, 0,
build_int_cst (arg0_type, 0), integer_one_node, 0);
NULL_TREE)) low = build_int_cst (arg0_type, 0);
break;
in_p = n_in_p, low = n_low, high = n_high;
/* If the high bound is missing, but we have a nonzero low
bound, reverse the range so it goes from zero to the low bound
minus 1. */
if (high == 0 && low && ! integer_zerop (low))
{
in_p = ! in_p;
high = range_binop (MINUS_EXPR, NULL_TREE, low, 0,
integer_one_node, 0);
low = build_int_cst (arg0_type, 0);
}
} }
}
exp = arg0; *p_low = low;
continue; *p_high = high;
*p_in_p = in_p;
case NEGATE_EXPR: return arg0;
/* (-x) IN [a,b] -> x in [-b, -a] */
n_low = range_binop (MINUS_EXPR, exp_type,
build_int_cst (exp_type, 0),
0, high, 1);
n_high = range_binop (MINUS_EXPR, exp_type,
build_int_cst (exp_type, 0),
0, low, 0);
if (n_high != 0 && TREE_OVERFLOW (n_high))
break;
goto normalize;
case BIT_NOT_EXPR: case NEGATE_EXPR:
/* ~ X -> -X - 1 */ /* (-x) IN [a,b] -> x in [-b, -a] */
exp = build2_loc (loc, MINUS_EXPR, exp_type, negate_expr (arg0), n_low = range_binop (MINUS_EXPR, exp_type,
build_int_cst (exp_type, 1)); build_int_cst (exp_type, 0),
continue; 0, high, 1);
n_high = range_binop (MINUS_EXPR, exp_type,
build_int_cst (exp_type, 0),
0, low, 0);
if (n_high != 0 && TREE_OVERFLOW (n_high))
return NULL_TREE;
goto normalize;
case PLUS_EXPR: case MINUS_EXPR: case BIT_NOT_EXPR:
if (TREE_CODE (arg1) != INTEGER_CST) /* ~ X -> -X - 1 */
break; return build2_loc (loc, MINUS_EXPR, exp_type, negate_expr (arg0),
build_int_cst (exp_type, 1));
/* If flag_wrapv and ARG0_TYPE is signed, then we cannot case PLUS_EXPR:
move a constant to the other side. */ case MINUS_EXPR:
if (!TYPE_UNSIGNED (arg0_type) if (TREE_CODE (arg1) != INTEGER_CST)
&& !TYPE_OVERFLOW_UNDEFINED (arg0_type)) return NULL_TREE;
break;
/* If EXP is signed, any overflow in the computation is undefined, /* If flag_wrapv and ARG0_TYPE is signed, then we cannot
so we don't worry about it so long as our computations on move a constant to the other side. */
the bounds don't overflow. For unsigned, overflow is defined if (!TYPE_UNSIGNED (arg0_type)
and this is exactly the right thing. */ && !TYPE_OVERFLOW_UNDEFINED (arg0_type))
n_low = range_binop (code == MINUS_EXPR ? PLUS_EXPR : MINUS_EXPR, return NULL_TREE;
arg0_type, low, 0, arg1, 0);
n_high = range_binop (code == MINUS_EXPR ? PLUS_EXPR : MINUS_EXPR,
arg0_type, high, 1, arg1, 0);
if ((n_low != 0 && TREE_OVERFLOW (n_low))
|| (n_high != 0 && TREE_OVERFLOW (n_high)))
break;
if (TYPE_OVERFLOW_UNDEFINED (arg0_type)) /* If EXP is signed, any overflow in the computation is undefined,
*strict_overflow_p = true; so we don't worry about it so long as our computations on
the bounds don't overflow. For unsigned, overflow is defined
and this is exactly the right thing. */
n_low = range_binop (code == MINUS_EXPR ? PLUS_EXPR : MINUS_EXPR,
arg0_type, low, 0, arg1, 0);
n_high = range_binop (code == MINUS_EXPR ? PLUS_EXPR : MINUS_EXPR,
arg0_type, high, 1, arg1, 0);
if ((n_low != 0 && TREE_OVERFLOW (n_low))
|| (n_high != 0 && TREE_OVERFLOW (n_high)))
return NULL_TREE;
normalize: if (TYPE_OVERFLOW_UNDEFINED (arg0_type))
/* Check for an unsigned range which has wrapped around the maximum *strict_overflow_p = true;
value thus making n_high < n_low, and normalize it. */
if (n_low && n_high && tree_int_cst_lt (n_high, n_low))
{
low = range_binop (PLUS_EXPR, arg0_type, n_high, 0,
integer_one_node, 0);
high = range_binop (MINUS_EXPR, arg0_type, n_low, 0,
integer_one_node, 0);
/* If the range is of the form +/- [ x+1, x ], we won't normalize:
be able to normalize it. But then, it represents the /* Check for an unsigned range which has wrapped around the maximum
whole range or the empty set, so make it value thus making n_high < n_low, and normalize it. */
+/- [ -, - ]. */ if (n_low && n_high && tree_int_cst_lt (n_high, n_low))
if (tree_int_cst_equal (n_low, low) {
&& tree_int_cst_equal (n_high, high)) low = range_binop (PLUS_EXPR, arg0_type, n_high, 0,
low = high = 0; integer_one_node, 0);
else high = range_binop (MINUS_EXPR, arg0_type, n_low, 0,
in_p = ! in_p; integer_one_node, 0);
}
else /* If the range is of the form +/- [ x+1, x ], we won't
low = n_low, high = n_high; be able to normalize it. But then, it represents the
whole range or the empty set, so make it
exp = arg0; +/- [ -, - ]. */
continue; if (tree_int_cst_equal (n_low, low)
&& tree_int_cst_equal (n_high, high))
low = high = 0;
else
in_p = ! in_p;
}
else
low = n_low, high = n_high;
CASE_CONVERT: case NON_LVALUE_EXPR: *p_low = low;
if (TYPE_PRECISION (arg0_type) > TYPE_PRECISION (exp_type)) *p_high = high;
break; *p_in_p = in_p;
return arg0;
if (! INTEGRAL_TYPE_P (arg0_type) CASE_CONVERT:
|| (low != 0 && ! int_fits_type_p (low, arg0_type)) case NON_LVALUE_EXPR:
|| (high != 0 && ! int_fits_type_p (high, arg0_type))) if (TYPE_PRECISION (arg0_type) > TYPE_PRECISION (exp_type))
break; return NULL_TREE;
n_low = low, n_high = high; if (! INTEGRAL_TYPE_P (arg0_type)
|| (low != 0 && ! int_fits_type_p (low, arg0_type))
|| (high != 0 && ! int_fits_type_p (high, arg0_type)))
return NULL_TREE;
if (n_low != 0) n_low = low, n_high = high;
n_low = fold_convert_loc (loc, arg0_type, n_low);
if (n_high != 0) if (n_low != 0)
n_high = fold_convert_loc (loc, arg0_type, n_high); n_low = fold_convert_loc (loc, arg0_type, n_low);
if (n_high != 0)
n_high = fold_convert_loc (loc, arg0_type, n_high);
/* If we're converting arg0 from an unsigned type, to exp, /* If we're converting arg0 from an unsigned type, to exp,
a signed type, we will be doing the comparison as unsigned. a signed type, we will be doing the comparison as unsigned.
The tests above have already verified that LOW and HIGH The tests above have already verified that LOW and HIGH
are both positive. are both positive.
So we have to ensure that we will handle large unsigned So we have to ensure that we will handle large unsigned
values the same way that the current signed bounds treat values the same way that the current signed bounds treat
negative values. */ negative values. */
if (!TYPE_UNSIGNED (exp_type) && TYPE_UNSIGNED (arg0_type)) if (!TYPE_UNSIGNED (exp_type) && TYPE_UNSIGNED (arg0_type))
{ {
tree high_positive; tree high_positive;
tree equiv_type; tree equiv_type;
/* For fixed-point modes, we need to pass the saturating flag /* For fixed-point modes, we need to pass the saturating flag
as the 2nd parameter. */ as the 2nd parameter. */
if (ALL_FIXED_POINT_MODE_P (TYPE_MODE (arg0_type))) if (ALL_FIXED_POINT_MODE_P (TYPE_MODE (arg0_type)))
equiv_type = lang_hooks.types.type_for_mode equiv_type
(TYPE_MODE (arg0_type), = lang_hooks.types.type_for_mode (TYPE_MODE (arg0_type),
TYPE_SATURATING (arg0_type)); TYPE_SATURATING (arg0_type));
else else
equiv_type = lang_hooks.types.type_for_mode equiv_type
(TYPE_MODE (arg0_type), 1); = lang_hooks.types.type_for_mode (TYPE_MODE (arg0_type), 1);
/* A range without an upper bound is, naturally, unbounded. /* A range without an upper bound is, naturally, unbounded.
Since convert would have cropped a very large value, use Since convert would have cropped a very large value, use
the max value for the destination type. */ the max value for the destination type. */
high_positive high_positive
= TYPE_MAX_VALUE (equiv_type) ? TYPE_MAX_VALUE (equiv_type) = TYPE_MAX_VALUE (equiv_type) ? TYPE_MAX_VALUE (equiv_type)
: TYPE_MAX_VALUE (arg0_type); : TYPE_MAX_VALUE (arg0_type);
if (TYPE_PRECISION (exp_type) == TYPE_PRECISION (arg0_type)) if (TYPE_PRECISION (exp_type) == TYPE_PRECISION (arg0_type))
high_positive = fold_build2_loc (loc, RSHIFT_EXPR, arg0_type, high_positive = fold_build2_loc (loc, RSHIFT_EXPR, arg0_type,
fold_convert_loc (loc, arg0_type, fold_convert_loc (loc, arg0_type,
high_positive), high_positive),
build_int_cst (arg0_type, 1)); build_int_cst (arg0_type, 1));
/* If the low bound is specified, "and" the range with the /* If the low bound is specified, "and" the range with the
range for which the original unsigned value will be range for which the original unsigned value will be
positive. */ positive. */
if (low != 0) if (low != 0)
{ {
if (! merge_ranges (&n_in_p, &n_low, &n_high, if (! merge_ranges (&n_in_p, &n_low, &n_high, 1, n_low, n_high,
1, n_low, n_high, 1, 1, fold_convert_loc (loc, arg0_type,
fold_convert_loc (loc, arg0_type, integer_zero_node),
integer_zero_node), high_positive))
high_positive)) return NULL_TREE;
break;
in_p = (n_in_p == in_p); in_p = (n_in_p == in_p);
} }
else else
{ {
/* Otherwise, "or" the range with the range of the input /* Otherwise, "or" the range with the range of the input
that will be interpreted as negative. */ that will be interpreted as negative. */
if (! merge_ranges (&n_in_p, &n_low, &n_high, if (! merge_ranges (&n_in_p, &n_low, &n_high, 0, n_low, n_high,
0, n_low, n_high, 1, 1, fold_convert_loc (loc, arg0_type,
fold_convert_loc (loc, arg0_type, integer_zero_node),
integer_zero_node), high_positive))
high_positive)) return NULL_TREE;
break;
in_p = (in_p != n_in_p); in_p = (in_p != n_in_p);
}
} }
}
exp = arg0; *p_low = n_low;
low = n_low, high = n_high; *p_high = n_high;
continue; *p_in_p = in_p;
return arg0;
default: default:
break; return NULL_TREE;
}
}
/* Given EXP, a logical expression, set the range it is testing into
variables denoted by PIN_P, PLOW, and PHIGH. Return the expression
actually being tested. *PLOW and *PHIGH will be made of the same
type as the returned expression. If EXP is not a comparison, we
will most likely not be returning a useful value and range. Set
*STRICT_OVERFLOW_P to true if the return value is only valid
because signed overflow is undefined; otherwise, do not change
*STRICT_OVERFLOW_P. */
tree
make_range (tree exp, int *pin_p, tree *plow, tree *phigh,
bool *strict_overflow_p)
{
enum tree_code code;
tree arg0, arg1 = NULL_TREE;
tree exp_type, nexp;
int in_p;
tree low, high;
location_t loc = EXPR_LOCATION (exp);
/* Start with simply saying "EXP != 0" and then look at the code of EXP
and see if we can refine the range. Some of the cases below may not
happen, but it doesn't seem worth worrying about this. We "continue"
the outer loop when we've changed something; otherwise we "break"
the switch, which will "break" the while. */
in_p = 0;
low = high = build_int_cst (TREE_TYPE (exp), 0);
while (1)
{
code = TREE_CODE (exp);
exp_type = TREE_TYPE (exp);
arg0 = NULL_TREE;
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)))
{
if (TREE_OPERAND_LENGTH (exp) > 0)
arg0 = TREE_OPERAND (exp, 0);
if (TREE_CODE_CLASS (code) == tcc_binary
|| TREE_CODE_CLASS (code) == tcc_comparison
|| (TREE_CODE_CLASS (code) == tcc_expression
&& TREE_OPERAND_LENGTH (exp) > 1))
arg1 = TREE_OPERAND (exp, 1);
} }
if (arg0 == NULL_TREE)
break;
break; nexp = make_range_step (loc, code, arg0, arg1, exp_type, &low,
&high, &in_p, strict_overflow_p);
if (nexp == NULL_TREE)
break;
exp = nexp;
} }
/* If EXP is a constant, we can evaluate whether this is true or false. */ /* If EXP is a constant, we can evaluate whether this is true or false. */
......
2011-09-30 Jakub Jelinek <jakub@redhat.com> 2011-09-30 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/46309
* gcc.dg/pr46309.c: New test.
2011-09-30 Jakub Jelinek <jakub@redhat.com>
* gcc.dg/strlenopt-21.c: New test. * gcc.dg/strlenopt-21.c: New test.
2011-09-30 Revital Eres <revital.eres@linaro.org> 2011-09-30 Revital Eres <revital.eres@linaro.org>
......
/* PR tree-optimization/46309 */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-reassoc-details" } */
int
f1 (int a)
{
int v1 = (a == 3);
int v2 = (a == 1);
int v3 = (a == 4);
int v4 = (a == 2);
return v1 || v2 || v3 || v4;
}
int
f2 (int a)
{
int v1 = (a == 1);
int v2 = (a == 2);
int v3 = (a == 3);
int v4 = (a == 4);
return v1 || v2 || v3 || v4;
}
int
f3 (int a)
{
int v1 = (a == 3);
int v2 = (a == 1);
return v1 || v2;
}
int
f4 (int a)
{
int v1 = (a == 1);
int v2 = (a == 2);
return v1 || v2;
}
int
f5 (unsigned int a)
{
int v1 = (a <= 31);
int v2 = (a >= 64 && a <= 95);
return v1 || v2;
}
int
f6 (unsigned int a)
{
int v1 = (a <= 31);
int v2 = (a >= 64 && a <= 95);
int v3 = (a >= 128 && a <= 159);
int v4 = (a >= 192 && a <= 223);
return v1 || v2 || v3 || v4;
}
/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. -.1, 1. and -.2, 2. and -.3, 3. and -.4, 4.\[\n\r\]* into" 2 "reassoc1" } } */
/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. -.1, 1. and -.3, 3.\[\n\r\]* into" 1 "reassoc1" } } */
/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. -.1, 1. and -.2, 2.\[\n\r\]* into" 1 "reassoc1" } } */
/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. -.0, 31. and -.64, 95.\[\n\r\]* into" 2 "reassoc1" } } */
/* { dg-final { scan-tree-dump-times "Optimizing range tests a_\[0-9\]*.D. -.128, 159. and -.192, 223.\[\n\r\]* into" 1 "reassoc1" } } */
/* { dg-final { scan-tree-dump-times "Optimizing range tests D.\[0-9\]*_\[0-9\]* -.0, 31. and -.128, 159.\[\n\r\]* into" 1 "reassoc2" } } */
/* { dg-final { cleanup-tree-dump "reassoc1" } } */
/* { dg-final { cleanup-tree-dump "reassoc2" } } */
...@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "flags.h" #include "flags.h"
#include "target.h" #include "target.h"
#include "params.h" #include "params.h"
#include "diagnostic-core.h"
/* This is a simple global reassociation pass. It is, in part, based /* This is a simple global reassociation pass. It is, in part, based
on the LLVM pass of the same name (They do some things more/less on the LLVM pass of the same name (They do some things more/less
...@@ -1568,6 +1569,457 @@ optimize_ops_list (enum tree_code opcode, ...@@ -1568,6 +1569,457 @@ optimize_ops_list (enum tree_code opcode,
optimize_ops_list (opcode, ops); optimize_ops_list (opcode, ops);
} }
/* The following functions are subroutines to optimize_range_tests and allow
it to try to change a logical combination of comparisons into a range
test.
For example, both
X == 2 || X == 5 || X == 3 || X == 4
and
X >= 2 && X <= 5
are converted to
(unsigned) (X - 2) <= 3
For more information see comments above fold_test_range in fold-const.c,
this implementation is for GIMPLE. */
struct range_entry
{
tree exp;
tree low;
tree high;
bool in_p;
bool strict_overflow_p;
unsigned int idx, next;
};
/* This is similar to make_range in fold-const.c, but on top of
GIMPLE instead of trees. */
static void
init_range_entry (struct range_entry *r, tree exp)
{
int in_p;
tree low, high;
bool is_bool, strict_overflow_p;
r->exp = NULL_TREE;
r->in_p = false;
r->strict_overflow_p = false;
r->low = NULL_TREE;
r->high = NULL_TREE;
if (TREE_CODE (exp) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (exp)))
return;
/* Start with simply saying "EXP != 0" and then look at the code of EXP
and see if we can refine the range. Some of the cases below may not
happen, but it doesn't seem worth worrying about this. We "continue"
the outer loop when we've changed something; otherwise we "break"
the switch, which will "break" the while. */
low = build_int_cst (TREE_TYPE (exp), 0);
high = low;
in_p = 0;
strict_overflow_p = false;
is_bool = false;
if (TYPE_PRECISION (TREE_TYPE (exp)) == 1)
{
if (TYPE_UNSIGNED (TREE_TYPE (exp)))
is_bool = true;
else
return;
}
else if (TREE_CODE (TREE_TYPE (exp)) == BOOLEAN_TYPE)
is_bool = true;
while (1)
{
gimple stmt;
enum tree_code code;
tree arg0, arg1, exp_type;
tree nexp;
location_t loc;
if (TREE_CODE (exp) != SSA_NAME)
break;
stmt = SSA_NAME_DEF_STMT (exp);
if (!is_gimple_assign (stmt))
break;
code = gimple_assign_rhs_code (stmt);
arg0 = gimple_assign_rhs1 (stmt);
arg1 = gimple_assign_rhs2 (stmt);
exp_type = TREE_TYPE (exp);
loc = gimple_location (stmt);
switch (code)
{
case BIT_NOT_EXPR:
if (TREE_CODE (TREE_TYPE (exp)) == BOOLEAN_TYPE)
{
in_p = !in_p;
exp = arg0;
continue;
}
break;
case SSA_NAME:
exp = arg0;
continue;
CASE_CONVERT:
if (is_bool)
goto do_default;
if (TYPE_PRECISION (TREE_TYPE (arg0)) == 1)
{
if (TYPE_UNSIGNED (TREE_TYPE (arg0)))
is_bool = true;
else
return;
}
else if (TREE_CODE (TREE_TYPE (arg0)) == BOOLEAN_TYPE)
is_bool = true;
goto do_default;
case EQ_EXPR:
case NE_EXPR:
case LT_EXPR:
case LE_EXPR:
case GE_EXPR:
case GT_EXPR:
is_bool = true;
/* FALLTHRU */
default:
if (!is_bool)
return;
do_default:
nexp = make_range_step (loc, code, arg0, arg1, exp_type,
&low, &high, &in_p,
&strict_overflow_p);
if (nexp != NULL_TREE)
{
exp = nexp;
gcc_assert (TREE_CODE (exp) == SSA_NAME);
continue;
}
break;
}
break;
}
if (is_bool)
{
r->exp = exp;
r->in_p = in_p;
r->low = low;
r->high = high;
r->strict_overflow_p = strict_overflow_p;
}
}
/* Comparison function for qsort. Sort entries
without SSA_NAME exp first, then with SSA_NAMEs sorted
by increasing SSA_NAME_VERSION, and for the same SSA_NAMEs
by increasing ->low and if ->low is the same, by increasing
->high. ->low == NULL_TREE means minimum, ->high == NULL_TREE
maximum. */
static int
range_entry_cmp (const void *a, const void *b)
{
const struct range_entry *p = (const struct range_entry *) a;
const struct range_entry *q = (const struct range_entry *) b;
if (p->exp != NULL_TREE && TREE_CODE (p->exp) == SSA_NAME)
{
if (q->exp != NULL_TREE && TREE_CODE (q->exp) == SSA_NAME)
{
/* Group range_entries for the same SSA_NAME together. */
if (SSA_NAME_VERSION (p->exp) < SSA_NAME_VERSION (q->exp))
return -1;
else if (SSA_NAME_VERSION (p->exp) > SSA_NAME_VERSION (q->exp))
return 1;
/* If ->low is different, NULL low goes first, then by
ascending low. */
if (p->low != NULL_TREE)
{
if (q->low != NULL_TREE)
{
tree tem = fold_binary (LT_EXPR, boolean_type_node,
p->low, q->low);
if (tem && integer_onep (tem))
return -1;
tem = fold_binary (GT_EXPR, boolean_type_node,
p->low, q->low);
if (tem && integer_onep (tem))
return 1;
}
else
return 1;
}
else if (q->low != NULL_TREE)
return -1;
/* If ->high is different, NULL high goes last, before that by
ascending high. */
if (p->high != NULL_TREE)
{
if (q->high != NULL_TREE)
{
tree tem = fold_binary (LT_EXPR, boolean_type_node,
p->high, q->high);
if (tem && integer_onep (tem))
return -1;
tem = fold_binary (GT_EXPR, boolean_type_node,
p->high, q->high);
if (tem && integer_onep (tem))
return 1;
}
else
return -1;
}
else if (p->high != NULL_TREE)
return 1;
/* If both ranges are the same, sort below by ascending idx. */
}
else
return 1;
}
else if (q->exp != NULL_TREE && TREE_CODE (q->exp) == SSA_NAME)
return -1;
if (p->idx < q->idx)
return -1;
else
{
gcc_checking_assert (p->idx > q->idx);
return 1;
}
}
/* Helper routine of optimize_range_test.
[EXP, IN_P, LOW, HIGH, STRICT_OVERFLOW_P] is a merged range for
RANGE and OTHERRANGE through OTHERRANGE + COUNT - 1 ranges,
OPCODE and OPS are arguments of optimize_range_tests. Return
true if the range merge has been successful. */
static bool
update_range_test (struct range_entry *range, struct range_entry *otherrange,
unsigned int count, enum tree_code opcode,
VEC (operand_entry_t, heap) **ops, tree exp, bool in_p,
tree low, tree high, bool strict_overflow_p)
{
tree op = VEC_index (operand_entry_t, *ops, range->idx)->op;
location_t loc = gimple_location (SSA_NAME_DEF_STMT (op));
tree tem = build_range_check (loc, TREE_TYPE (op), exp, in_p, low, high);
enum warn_strict_overflow_code wc = WARN_STRICT_OVERFLOW_COMPARISON;
gimple_stmt_iterator gsi;
if (tem == NULL_TREE)
return false;
if (strict_overflow_p && issue_strict_overflow_warning (wc))
warning_at (loc, OPT_Wstrict_overflow,
"assuming signed overflow does not occur "
"when simplifying range test");
if (dump_file && (dump_flags & TDF_DETAILS))
{
struct range_entry *r;
fprintf (dump_file, "Optimizing range tests ");
print_generic_expr (dump_file, range->exp, 0);
fprintf (dump_file, " %c[", range->in_p ? '+' : '-');
print_generic_expr (dump_file, range->low, 0);
fprintf (dump_file, ", ");
print_generic_expr (dump_file, range->high, 0);
fprintf (dump_file, "]");
for (r = otherrange; r < otherrange + count; r++)
{
fprintf (dump_file, " and %c[", r->in_p ? '+' : '-');
print_generic_expr (dump_file, r->low, 0);
fprintf (dump_file, ", ");
print_generic_expr (dump_file, r->high, 0);
fprintf (dump_file, "]");
}
fprintf (dump_file, "\n into ");
print_generic_expr (dump_file, tem, 0);
fprintf (dump_file, "\n");
}
if (opcode == BIT_IOR_EXPR)
tem = invert_truthvalue_loc (loc, tem);
tem = fold_convert_loc (loc, TREE_TYPE (op), tem);
gsi = gsi_for_stmt (SSA_NAME_DEF_STMT (op));
tem = force_gimple_operand_gsi (&gsi, tem, true, NULL_TREE, true,
GSI_SAME_STMT);
VEC_index (operand_entry_t, *ops, range->idx)->op = tem;
range->exp = exp;
range->low = low;
range->high = high;
range->in_p = in_p;
range->strict_overflow_p = false;
for (range = otherrange; range < otherrange + count; range++)
{
VEC_index (operand_entry_t, *ops, range->idx)->op = error_mark_node;
range->exp = NULL_TREE;
}
return true;
}
/* Optimize range tests, similarly how fold_range_test optimizes
it on trees. The tree code for the binary
operation between all the operands is OPCODE. */
static void
optimize_range_tests (enum tree_code opcode,
VEC (operand_entry_t, heap) **ops)
{
unsigned int length = VEC_length (operand_entry_t, *ops), i, j, first;
operand_entry_t oe;
struct range_entry *ranges;
bool any_changes = false;
if (length == 1)
return;
ranges = XNEWVEC (struct range_entry, length);
for (i = 0; i < length; i++)
{
ranges[i].idx = i;
init_range_entry (ranges + i, VEC_index (operand_entry_t, *ops, i)->op);
/* For | invert it now, we will invert it again before emitting
the optimized expression. */
if (opcode == BIT_IOR_EXPR)
ranges[i].in_p = !ranges[i].in_p;
}
qsort (ranges, length, sizeof (*ranges), range_entry_cmp);
for (i = 0; i < length; i++)
if (ranges[i].exp != NULL_TREE && TREE_CODE (ranges[i].exp) == SSA_NAME)
break;
/* Try to merge ranges. */
for (first = i; i < length; i++)
{
tree low = ranges[i].low;
tree high = ranges[i].high;
int in_p = ranges[i].in_p;
bool strict_overflow_p = ranges[i].strict_overflow_p;
int update_fail_count = 0;
for (j = i + 1; j < length; j++)
{
if (ranges[i].exp != ranges[j].exp)
break;
if (!merge_ranges (&in_p, &low, &high, in_p, low, high,
ranges[j].in_p, ranges[j].low, ranges[j].high))
break;
strict_overflow_p |= ranges[j].strict_overflow_p;
}
if (j == i + 1)
continue;
if (update_range_test (ranges + i, ranges + i + 1, j - i - 1, opcode,
ops, ranges[i].exp, in_p, low, high,
strict_overflow_p))
{
i = j - 1;
any_changes = true;
}
/* Avoid quadratic complexity if all merge_ranges calls would succeed,
while update_range_test would fail. */
else if (update_fail_count == 64)
i = j - 1;
else
++update_fail_count;
}
/* Optimize X == CST1 || X == CST2
if popcount (CST1 ^ CST2) == 1 into
(X & ~(CST1 ^ CST2)) == (CST1 & ~(CST1 ^ CST2)).
Similarly for ranges. E.g.
X != 2 && X != 3 && X != 10 && X != 11
will be transformed by the above loop into
(X - 2U) <= 1U && (X - 10U) <= 1U
and this loop can transform that into
((X & ~8) - 2U) <= 1U. */
for (i = first; i < length; i++)
{
tree lowi, highi, lowj, highj, type, lowxor, highxor, tem, exp;
if (ranges[i].exp == NULL_TREE || ranges[i].in_p)
continue;
type = TREE_TYPE (ranges[i].exp);
if (!INTEGRAL_TYPE_P (type))
continue;
lowi = ranges[i].low;
if (lowi == NULL_TREE)
lowi = TYPE_MIN_VALUE (type);
highi = ranges[i].high;
if (highi == NULL_TREE)
continue;
for (j = i + 1; j < length && j < i + 64; j++)
{
if (ranges[j].exp == NULL_TREE)
continue;
if (ranges[i].exp != ranges[j].exp)
break;
if (ranges[j].in_p)
continue;
lowj = ranges[j].low;
if (lowj == NULL_TREE)
continue;
highj = ranges[j].high;
if (highj == NULL_TREE)
highj = TYPE_MAX_VALUE (type);
tem = fold_binary (GT_EXPR, boolean_type_node,
lowj, highi);
if (tem == NULL_TREE || !integer_onep (tem))
continue;
lowxor = fold_binary (BIT_XOR_EXPR, type, lowi, lowj);
if (lowxor == NULL_TREE || TREE_CODE (lowxor) != INTEGER_CST)
continue;
gcc_checking_assert (!integer_zerop (lowxor));
tem = fold_binary (MINUS_EXPR, type, lowxor,
build_int_cst (type, 1));
if (tem == NULL_TREE)
continue;
tem = fold_binary (BIT_AND_EXPR, type, lowxor, tem);
if (tem == NULL_TREE || !integer_zerop (tem))
continue;
highxor = fold_binary (BIT_XOR_EXPR, type, highi, highj);
if (!tree_int_cst_equal (lowxor, highxor))
continue;
tem = fold_build1 (BIT_NOT_EXPR, type, lowxor);
exp = fold_build2 (BIT_AND_EXPR, type, ranges[i].exp, tem);
lowj = fold_build2 (BIT_AND_EXPR, type, lowi, tem);
highj = fold_build2 (BIT_AND_EXPR, type, highi, tem);
if (update_range_test (ranges + i, ranges + j, 1, opcode, ops, exp,
ranges[i].in_p, lowj, highj,
ranges[i].strict_overflow_p
|| ranges[j].strict_overflow_p))
{
any_changes = true;
break;
}
}
}
if (any_changes)
{
j = 0;
FOR_EACH_VEC_ELT (operand_entry_t, *ops, i, oe)
{
if (oe->op == error_mark_node)
continue;
else if (i != j)
VEC_replace (operand_entry_t, *ops, j, oe);
j++;
}
VEC_truncate (operand_entry_t, *ops, j);
}
XDELETEVEC (ranges);
}
/* Return true if OPERAND is defined by a PHI node which uses the LHS /* Return true if OPERAND is defined by a PHI node which uses the LHS
of STMT in it's operands. This is also known as a "destructive of STMT in it's operands. This is also known as a "destructive
update" operation. */ update" operation. */
...@@ -2447,6 +2899,9 @@ reassociate_bb (basic_block bb) ...@@ -2447,6 +2899,9 @@ reassociate_bb (basic_block bb)
optimize_ops_list (rhs_code, &ops); optimize_ops_list (rhs_code, &ops);
} }
if (rhs_code == BIT_IOR_EXPR || rhs_code == BIT_AND_EXPR)
optimize_range_tests (rhs_code, &ops);
if (VEC_length (operand_entry_t, ops) == 1) if (VEC_length (operand_entry_t, ops) == 1)
{ {
if (dump_file && (dump_flags & TDF_DETAILS)) if (dump_file && (dump_flags & TDF_DETAILS))
......
...@@ -5384,6 +5384,8 @@ extern unsigned int get_pointer_alignment (tree); ...@@ -5384,6 +5384,8 @@ extern unsigned int get_pointer_alignment (tree);
extern tree fold_call_stmt (gimple, bool); extern tree fold_call_stmt (gimple, bool);
extern tree gimple_fold_builtin_snprintf_chk (gimple, tree, enum built_in_function); extern tree gimple_fold_builtin_snprintf_chk (gimple, tree, enum built_in_function);
extern tree make_range (tree, int *, tree *, tree *, bool *); 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 *);
extern tree build_range_check (location_t, tree, tree, int, tree, tree); extern tree build_range_check (location_t, tree, tree, int, tree, tree);
extern bool merge_ranges (int *, tree *, tree *, int, tree, tree, int, extern bool merge_ranges (int *, tree *, tree *, int, tree, tree, int,
tree, tree); tree, tree);
......
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