summaryrefslogtreecommitdiff
path: root/gcc/fold-const.c
diff options
context:
space:
mode:
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>2011-09-30 15:00:12 +0000
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>2011-09-30 15:00:12 +0000
commit946e9eb4bfc3217f81eb13ba648a2840bfcb31ee (patch)
tree573817e015f2034176cc6127bcda920e8e5f7953 /gcc/fold-const.c
parent77efe8191baab106cf0444cf66cf9d466d2b742a (diff)
downloadgcc-946e9eb4bfc3217f81eb13ba648a2840bfcb31ee.tar.gz
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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@179388 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/fold-const.c')
-rw-r--r--gcc/fold-const.c503
1 files changed, 260 insertions, 243 deletions
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 2726e018665..79ebd8b5701 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -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_predecessor (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_cond_expr_with_comparison (location_t, tree, tree, tree, 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,
return constant_boolean_node (result, type);
}
-/* 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. */
+/* Helper routine for make_range. Perform one step for it, return
+ new expression if the loop should continue or NULL_TREE if it should
+ stop. */
tree
-make_range (tree exp, int *pin_p, tree *plow, tree *phigh,
- bool *strict_overflow_p)
+make_range_step (location_t loc, enum tree_code code, tree arg0, tree arg1,
+ tree exp_type, tree *p_low, tree *p_high, int *p_in_p,
+ bool *strict_overflow_p)
{
- enum tree_code code;
- tree arg0 = NULL_TREE, arg1 = NULL_TREE;
- tree exp_type = NULL_TREE, arg0_type = NULL_TREE;
- 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);
+ tree arg0_type = TREE_TYPE (arg0);
+ tree n_low, n_high, low = *p_low, high = *p_high;
+ int in_p = *p_in_p, n_in_p;
- while (1)
+ switch (code)
{
- code = TREE_CODE (exp);
- exp_type = TREE_TYPE (exp);
+ case TRUTH_NOT_EXPR:
+ *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)
- arg0 = TREE_OPERAND (exp, 0);
- if (TREE_CODE_CLASS (code) == tcc_comparison
- || TREE_CODE_CLASS (code) == tcc_unary
- || TREE_CODE_CLASS (code) == tcc_binary)
- arg0_type = TREE_TYPE (arg0);
- 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);
+ 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 ();
}
- 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:
- in_p = ! in_p, exp = arg0;
- continue;
-
- 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 == 0 || high == 0
- || ! integer_zerop (low) || ! integer_zerop (high)
- || TREE_CODE (arg1) != INTEGER_CST)
- break;
+ if (! merge_ranges (&n_in_p, &n_low, &n_high,
+ in_p, low, high, 1,
+ build_int_cst (arg0_type, 0),
+ NULL_TREE))
+ return NULL_TREE;
- switch (code)
- {
- 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 ();
- }
+ in_p = n_in_p, low = n_low, high = n_high;
- /* 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))
+ /* 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))
{
- if (! merge_ranges (&n_in_p, &n_low, &n_high,
- in_p, low, high, 1,
- build_int_cst (arg0_type, 0),
- NULL_TREE))
- 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);
- }
+ 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;
- continue;
-
- case NEGATE_EXPR:
- /* (-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;
+ *p_low = low;
+ *p_high = high;
+ *p_in_p = in_p;
+ return arg0;
- case BIT_NOT_EXPR:
- /* ~ X -> -X - 1 */
- exp = build2_loc (loc, MINUS_EXPR, exp_type, negate_expr (arg0),
- build_int_cst (exp_type, 1));
- continue;
+ case NEGATE_EXPR:
+ /* (-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))
+ return NULL_TREE;
+ goto normalize;
- case PLUS_EXPR: case MINUS_EXPR:
- if (TREE_CODE (arg1) != INTEGER_CST)
- break;
+ case BIT_NOT_EXPR:
+ /* ~ X -> -X - 1 */
+ 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
- move a constant to the other side. */
- if (!TYPE_UNSIGNED (arg0_type)
- && !TYPE_OVERFLOW_UNDEFINED (arg0_type))
- break;
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ if (TREE_CODE (arg1) != INTEGER_CST)
+ return NULL_TREE;
- /* If EXP is signed, any overflow in the computation is undefined,
- 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)))
- break;
+ /* If flag_wrapv and ARG0_TYPE is signed, then we cannot
+ move a constant to the other side. */
+ if (!TYPE_UNSIGNED (arg0_type)
+ && !TYPE_OVERFLOW_UNDEFINED (arg0_type))
+ return NULL_TREE;
- if (TYPE_OVERFLOW_UNDEFINED (arg0_type))
- *strict_overflow_p = true;
+ /* If EXP is signed, any overflow in the computation is undefined,
+ 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:
- /* Check for an unsigned range which has wrapped around the maximum
- 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 (TYPE_OVERFLOW_UNDEFINED (arg0_type))
+ *strict_overflow_p = true;
- /* If the range is of the form +/- [ x+1, x ], we won't
- be able to normalize it. But then, it represents the
- whole range or the empty set, so make it
- +/- [ -, - ]. */
- 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;
-
- exp = arg0;
- continue;
+ normalize:
+ /* Check for an unsigned range which has wrapped around the maximum
+ 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
+ be able to normalize it. But then, it represents the
+ whole range or the empty set, so make it
+ +/- [ -, - ]. */
+ 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:
- if (TYPE_PRECISION (arg0_type) > TYPE_PRECISION (exp_type))
- break;
+ *p_low = low;
+ *p_high = high;
+ *p_in_p = in_p;
+ return arg0;
- if (! INTEGRAL_TYPE_P (arg0_type)
- || (low != 0 && ! int_fits_type_p (low, arg0_type))
- || (high != 0 && ! int_fits_type_p (high, arg0_type)))
- break;
+ CASE_CONVERT:
+ case NON_LVALUE_EXPR:
+ if (TYPE_PRECISION (arg0_type) > TYPE_PRECISION (exp_type))
+ 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 = fold_convert_loc (loc, arg0_type, n_low);
+ n_low = low, n_high = high;
- if (n_high != 0)
- n_high = fold_convert_loc (loc, arg0_type, n_high);
+ if (n_low != 0)
+ 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,
- a signed type, we will be doing the comparison as unsigned.
- The tests above have already verified that LOW and HIGH
- are both positive.
+ /* If we're converting arg0 from an unsigned type, to exp,
+ a signed type, we will be doing the comparison as unsigned.
+ The tests above have already verified that LOW and HIGH
+ are both positive.
- So we have to ensure that we will handle large unsigned
- values the same way that the current signed bounds treat
- negative values. */
+ So we have to ensure that we will handle large unsigned
+ values the same way that the current signed bounds treat
+ negative values. */
- if (!TYPE_UNSIGNED (exp_type) && TYPE_UNSIGNED (arg0_type))
- {
- tree high_positive;
- tree equiv_type;
- /* For fixed-point modes, we need to pass the saturating flag
- as the 2nd parameter. */
- if (ALL_FIXED_POINT_MODE_P (TYPE_MODE (arg0_type)))
- equiv_type = lang_hooks.types.type_for_mode
- (TYPE_MODE (arg0_type),
- TYPE_SATURATING (arg0_type));
- else
- equiv_type = lang_hooks.types.type_for_mode
- (TYPE_MODE (arg0_type), 1);
-
- /* A range without an upper bound is, naturally, unbounded.
- Since convert would have cropped a very large value, use
- the max value for the destination type. */
- high_positive
- = TYPE_MAX_VALUE (equiv_type) ? TYPE_MAX_VALUE (equiv_type)
- : TYPE_MAX_VALUE (arg0_type);
-
- if (TYPE_PRECISION (exp_type) == TYPE_PRECISION (arg0_type))
- high_positive = fold_build2_loc (loc, RSHIFT_EXPR, arg0_type,
+ if (!TYPE_UNSIGNED (exp_type) && TYPE_UNSIGNED (arg0_type))
+ {
+ tree high_positive;
+ tree equiv_type;
+ /* For fixed-point modes, we need to pass the saturating flag
+ as the 2nd parameter. */
+ if (ALL_FIXED_POINT_MODE_P (TYPE_MODE (arg0_type)))
+ equiv_type
+ = lang_hooks.types.type_for_mode (TYPE_MODE (arg0_type),
+ TYPE_SATURATING (arg0_type));
+ else
+ equiv_type
+ = lang_hooks.types.type_for_mode (TYPE_MODE (arg0_type), 1);
+
+ /* A range without an upper bound is, naturally, unbounded.
+ Since convert would have cropped a very large value, use
+ the max value for the destination type. */
+ high_positive
+ = TYPE_MAX_VALUE (equiv_type) ? TYPE_MAX_VALUE (equiv_type)
+ : TYPE_MAX_VALUE (arg0_type);
+
+ if (TYPE_PRECISION (exp_type) == TYPE_PRECISION (arg0_type))
+ high_positive = fold_build2_loc (loc, RSHIFT_EXPR, arg0_type,
fold_convert_loc (loc, arg0_type,
high_positive),
build_int_cst (arg0_type, 1));
- /* If the low bound is specified, "and" the range with the
- range for which the original unsigned value will be
- positive. */
- if (low != 0)
- {
- if (! merge_ranges (&n_in_p, &n_low, &n_high,
- 1, n_low, n_high, 1,
- fold_convert_loc (loc, arg0_type,
- integer_zero_node),
- high_positive))
- break;
+ /* If the low bound is specified, "and" the range with the
+ range for which the original unsigned value will be
+ positive. */
+ if (low != 0)
+ {
+ if (! merge_ranges (&n_in_p, &n_low, &n_high, 1, n_low, n_high,
+ 1, fold_convert_loc (loc, arg0_type,
+ integer_zero_node),
+ high_positive))
+ return NULL_TREE;
- in_p = (n_in_p == in_p);
- }
- else
- {
- /* Otherwise, "or" the range with the range of the input
- that will be interpreted as negative. */
- if (! merge_ranges (&n_in_p, &n_low, &n_high,
- 0, n_low, n_high, 1,
- fold_convert_loc (loc, arg0_type,
- integer_zero_node),
- high_positive))
- break;
+ in_p = (n_in_p == in_p);
+ }
+ else
+ {
+ /* Otherwise, "or" the range with the range of the input
+ that will be interpreted as negative. */
+ if (! merge_ranges (&n_in_p, &n_low, &n_high, 0, n_low, n_high,
+ 1, fold_convert_loc (loc, arg0_type,
+ integer_zero_node),
+ high_positive))
+ return NULL_TREE;
- in_p = (in_p != n_in_p);
- }
+ in_p = (in_p != n_in_p);
}
+ }
- exp = arg0;
- low = n_low, high = n_high;
- continue;
+ *p_low = n_low;
+ *p_high = n_high;
+ *p_in_p = in_p;
+ return arg0;
- default:
- break;
+ default:
+ 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. */