mirror of
https://gcc.gnu.org/git/gcc.git
synced 2025-01-12 14:04:22 +08:00
re PR tree-optimization/27865 (tree check failure building FreePOOMA)
PR tree-optimization/27865 * tree-vrp.c (adjust_range_with_scev): Do not use TYPE_{MIN,MAX}_VALUE for pointer types. * tree-scalar-evolution.c (fold_used_pointer_cast, pointer_offset_p, fold_used_pointer, pointer_used_p): New functions. (analyze_scalar_evolution_1): Use fold_used_pointer. * tree-chrec.c (convert_affine_scev): Convert no-op casts correctly. * tree-ssa-loop-ivopts.c (generic_type_for): Return integral type for pointers. From-SVN: r116213
This commit is contained in:
parent
b646edb85c
commit
2052721560
@ -1,3 +1,15 @@
|
||||
2006-08-16 Zdenek Dvorak <dvorakz@suse.cz>
|
||||
|
||||
PR tree-optimization/27865
|
||||
* tree-vrp.c (adjust_range_with_scev): Do not use TYPE_{MIN,MAX}_VALUE
|
||||
for pointer types.
|
||||
* tree-scalar-evolution.c (fold_used_pointer_cast, pointer_offset_p,
|
||||
fold_used_pointer, pointer_used_p): New functions.
|
||||
(analyze_scalar_evolution_1): Use fold_used_pointer.
|
||||
* tree-chrec.c (convert_affine_scev): Convert no-op casts correctly.
|
||||
* tree-ssa-loop-ivopts.c (generic_type_for): Return integral type
|
||||
for pointers.
|
||||
|
||||
2006-08-17 Paolo Bonzini <bonzini@gnu.org>
|
||||
|
||||
PR c++/28573
|
||||
|
@ -1162,7 +1162,10 @@ convert_affine_scev (struct loop *loop, tree type,
|
||||
-- must_check_src_overflow is true, and the range of TYPE is superset
|
||||
of the range of CT -- i.e., in all cases except if CT signed and
|
||||
TYPE unsigned.
|
||||
-- both CT and TYPE have the same precision and signedness. */
|
||||
-- both CT and TYPE have the same precision and signedness, and we
|
||||
verify instead that the source does not overflow (this may be
|
||||
easier than verifying it for the result, as we may use the
|
||||
information about the semantics of overflow in CT). */
|
||||
if (must_check_src_overflow)
|
||||
{
|
||||
if (TYPE_UNSIGNED (type) && !TYPE_UNSIGNED (ct))
|
||||
@ -1172,7 +1175,10 @@ convert_affine_scev (struct loop *loop, tree type,
|
||||
}
|
||||
else if (TYPE_UNSIGNED (ct) == TYPE_UNSIGNED (type)
|
||||
&& TYPE_PRECISION (ct) == TYPE_PRECISION (type))
|
||||
must_check_rslt_overflow = false;
|
||||
{
|
||||
must_check_rslt_overflow = false;
|
||||
must_check_src_overflow = true;
|
||||
}
|
||||
else
|
||||
must_check_rslt_overflow = true;
|
||||
}
|
||||
|
@ -1718,6 +1718,184 @@ compute_scalar_evolution_in_loop (struct loop *wrto_loop,
|
||||
return analyze_scalar_evolution_1 (wrto_loop, res, chrec_not_analyzed_yet);
|
||||
}
|
||||
|
||||
/* Folds EXPR, if it is a cast to pointer, assuming that the created
|
||||
polynomial_chrec does not wrap. */
|
||||
|
||||
static tree
|
||||
fold_used_pointer_cast (tree expr)
|
||||
{
|
||||
tree op;
|
||||
tree type, inner_type;
|
||||
|
||||
if (TREE_CODE (expr) != NOP_EXPR && TREE_CODE (expr) != CONVERT_EXPR)
|
||||
return expr;
|
||||
|
||||
op = TREE_OPERAND (expr, 0);
|
||||
if (TREE_CODE (op) != POLYNOMIAL_CHREC)
|
||||
return expr;
|
||||
|
||||
type = TREE_TYPE (expr);
|
||||
inner_type = TREE_TYPE (op);
|
||||
|
||||
if (!INTEGRAL_TYPE_P (inner_type)
|
||||
|| TYPE_PRECISION (inner_type) != TYPE_PRECISION (type))
|
||||
return expr;
|
||||
|
||||
return build_polynomial_chrec (CHREC_VARIABLE (op),
|
||||
chrec_convert (type, CHREC_LEFT (op), NULL_TREE),
|
||||
chrec_convert (type, CHREC_RIGHT (op), NULL_TREE));
|
||||
}
|
||||
|
||||
/* Returns true if EXPR is an expression corresponding to offset of pointer
|
||||
in p + offset. */
|
||||
|
||||
static bool
|
||||
pointer_offset_p (tree expr)
|
||||
{
|
||||
if (TREE_CODE (expr) == INTEGER_CST)
|
||||
return true;
|
||||
|
||||
if ((TREE_CODE (expr) == NOP_EXPR || TREE_CODE (expr) == CONVERT_EXPR)
|
||||
&& INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0))))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* EXPR is a scalar evolution of a pointer that is dereferenced or used in
|
||||
comparison. This means that it must point to a part of some object in
|
||||
memory, which enables us to argue about overflows and possibly simplify
|
||||
the EXPR. Returns the simplified value.
|
||||
|
||||
Currently, for
|
||||
|
||||
int i, n;
|
||||
int *p;
|
||||
|
||||
for (i = -n; i < n; i++)
|
||||
*(p + i) = ...;
|
||||
|
||||
We generate the following code (assuming that size of int and size_t is
|
||||
4 bytes):
|
||||
|
||||
for (i = -n; i < n; i++)
|
||||
{
|
||||
size_t tmp1, tmp2;
|
||||
int *tmp3, *tmp4;
|
||||
|
||||
tmp1 = (size_t) i; (1)
|
||||
tmp2 = 4 * tmp1; (2)
|
||||
tmp3 = (int *) tmp2; (3)
|
||||
tmp4 = p + tmp3; (4)
|
||||
|
||||
*tmp4 = ...;
|
||||
}
|
||||
|
||||
We in general assume that pointer arithmetics does not overflow (since its
|
||||
behavior is undefined in that case). One of the problems is that our
|
||||
translation does not capture this property very well -- (int *) is
|
||||
considered unsigned, hence the computation in (4) does overflow if i is
|
||||
negative.
|
||||
|
||||
This impreciseness creates complications in scev analysis. The scalar
|
||||
evolution of i is [-n, +, 1]. Since int and size_t have the same precision
|
||||
(in this example), and size_t is unsigned (so we do not care about
|
||||
overflows), we succeed to derive that scev of tmp1 is [(size_t) -n, +, 1]
|
||||
and scev of tmp2 is [4 * (size_t) -n, +, 4]. With tmp3, we run into
|
||||
problem -- [(int *) (4 * (size_t) -n), +, 4] wraps, and since we on several
|
||||
places assume that this is not the case for scevs with pointer type, we
|
||||
cannot use this scev for tmp3; hence, its scev is
|
||||
(int *) [(4 * (size_t) -n), +, 4], and scev of tmp4 is
|
||||
p + (int *) [(4 * (size_t) -n), +, 4]. Most of the optimizers are unable to
|
||||
work with scevs of this shape.
|
||||
|
||||
However, since tmp4 is dereferenced, all its values must belong to a single
|
||||
object, and taking into account that the precision of int * and size_t is
|
||||
the same, it is impossible for its scev to wrap. Hence, we can derive that
|
||||
its evolution is [p + (int *) (4 * (size_t) -n), +, 4], which the optimizers
|
||||
can work with.
|
||||
|
||||
??? Maybe we should use different representation for pointer arithmetics,
|
||||
however that is a long-term project with a lot of potential for creating
|
||||
bugs. */
|
||||
|
||||
static tree
|
||||
fold_used_pointer (tree expr)
|
||||
{
|
||||
tree op0, op1, new0, new1;
|
||||
enum tree_code code = TREE_CODE (expr);
|
||||
|
||||
if (code == PLUS_EXPR
|
||||
|| code == MINUS_EXPR)
|
||||
{
|
||||
op0 = TREE_OPERAND (expr, 0);
|
||||
op1 = TREE_OPERAND (expr, 1);
|
||||
|
||||
if (pointer_offset_p (op1))
|
||||
{
|
||||
new0 = fold_used_pointer (op0);
|
||||
new1 = fold_used_pointer_cast (op1);
|
||||
}
|
||||
else if (code == PLUS_EXPR && pointer_offset_p (op0))
|
||||
{
|
||||
new0 = fold_used_pointer_cast (op0);
|
||||
new1 = fold_used_pointer (op1);
|
||||
}
|
||||
else
|
||||
return expr;
|
||||
|
||||
if (new0 == op0 && new1 == op1)
|
||||
return expr;
|
||||
|
||||
if (code == PLUS_EXPR)
|
||||
expr = chrec_fold_plus (TREE_TYPE (expr), new0, new1);
|
||||
else
|
||||
expr = chrec_fold_minus (TREE_TYPE (expr), new0, new1);
|
||||
|
||||
return expr;
|
||||
}
|
||||
else
|
||||
return fold_used_pointer_cast (expr);
|
||||
}
|
||||
|
||||
/* Returns true if PTR is dereferenced, or used in comparison. */
|
||||
|
||||
static bool
|
||||
pointer_used_p (tree ptr)
|
||||
{
|
||||
use_operand_p use_p;
|
||||
imm_use_iterator imm_iter;
|
||||
tree stmt, rhs;
|
||||
struct ptr_info_def *pi = get_ptr_info (ptr);
|
||||
var_ann_t v_ann = var_ann (SSA_NAME_VAR (ptr));
|
||||
|
||||
/* Check whether the pointer has a memory tag; if it does, it is
|
||||
(or at least used to be) dereferenced. */
|
||||
if ((pi != NULL && pi->name_mem_tag != NULL)
|
||||
|| v_ann->symbol_mem_tag)
|
||||
return true;
|
||||
|
||||
FOR_EACH_IMM_USE_FAST (use_p, imm_iter, ptr)
|
||||
{
|
||||
stmt = USE_STMT (use_p);
|
||||
if (TREE_CODE (stmt) == COND_EXPR)
|
||||
return true;
|
||||
|
||||
if (TREE_CODE (stmt) != MODIFY_EXPR)
|
||||
continue;
|
||||
|
||||
rhs = TREE_OPERAND (stmt, 1);
|
||||
if (!COMPARISON_CLASS_P (rhs))
|
||||
continue;
|
||||
|
||||
if (TREE_OPERAND (stmt, 0) == ptr
|
||||
|| TREE_OPERAND (stmt, 1) == ptr)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Helper recursive function. */
|
||||
|
||||
static tree
|
||||
@ -1766,6 +1944,11 @@ analyze_scalar_evolution_1 (struct loop *loop, tree var, tree res)
|
||||
{
|
||||
case MODIFY_EXPR:
|
||||
res = interpret_rhs_modify_expr (loop, def, TREE_OPERAND (def, 1), type);
|
||||
|
||||
if (POINTER_TYPE_P (type)
|
||||
&& !automatically_generated_chrec_p (res)
|
||||
&& pointer_used_p (var))
|
||||
res = fold_used_pointer (res);
|
||||
break;
|
||||
|
||||
case PHI_NODE:
|
||||
|
@ -1925,14 +1925,14 @@ strip_offset (tree expr, unsigned HOST_WIDE_INT *offset)
|
||||
}
|
||||
|
||||
/* Returns variant of TYPE that can be used as base for different uses.
|
||||
For integer types, we return unsigned variant of the type, which
|
||||
avoids problems with overflows. For pointer types, we return void *. */
|
||||
We return unsigned type with the same precision, which avoids problems
|
||||
with overflows. */
|
||||
|
||||
static tree
|
||||
generic_type_for (tree type)
|
||||
{
|
||||
if (POINTER_TYPE_P (type))
|
||||
return ptr_type_node;
|
||||
return unsigned_type_for (type);
|
||||
|
||||
if (TYPE_UNSIGNED (type))
|
||||
return type;
|
||||
|
@ -2015,7 +2015,7 @@ static void
|
||||
adjust_range_with_scev (value_range_t *vr, struct loop *loop, tree stmt,
|
||||
tree var)
|
||||
{
|
||||
tree init, step, chrec;
|
||||
tree init, step, chrec, tmin, tmax, min, max, type;
|
||||
enum ev_direction dir;
|
||||
|
||||
/* TODO. Don't adjust anti-ranges. An anti-range may provide
|
||||
@ -2049,13 +2049,23 @@ adjust_range_with_scev (value_range_t *vr, struct loop *loop, tree stmt,
|
||||
true))
|
||||
return;
|
||||
|
||||
if (!POINTER_TYPE_P (TREE_TYPE (init))
|
||||
&& (vr->type == VR_VARYING || vr->type == VR_UNDEFINED))
|
||||
type = TREE_TYPE (var);
|
||||
if (POINTER_TYPE_P (type) || !TYPE_MIN_VALUE (type))
|
||||
tmin = lower_bound_in_type (type, type);
|
||||
else
|
||||
tmin = TYPE_MIN_VALUE (type);
|
||||
if (POINTER_TYPE_P (type) || !TYPE_MAX_VALUE (type))
|
||||
tmax = upper_bound_in_type (type, type);
|
||||
else
|
||||
tmax = TYPE_MAX_VALUE (type);
|
||||
|
||||
if (vr->type == VR_VARYING || vr->type == VR_UNDEFINED)
|
||||
{
|
||||
min = tmin;
|
||||
max = tmax;
|
||||
|
||||
/* For VARYING or UNDEFINED ranges, just about anything we get
|
||||
from scalar evolutions should be better. */
|
||||
tree min = TYPE_MIN_VALUE (TREE_TYPE (init));
|
||||
tree max = TYPE_MAX_VALUE (TREE_TYPE (init));
|
||||
|
||||
if (dir == EV_DIR_DECREASES)
|
||||
max = init;
|
||||
@ -2064,7 +2074,8 @@ adjust_range_with_scev (value_range_t *vr, struct loop *loop, tree stmt,
|
||||
|
||||
/* If we would create an invalid range, then just assume we
|
||||
know absolutely nothing. This may be over-conservative,
|
||||
but it's clearly safe. */
|
||||
but it's clearly safe, and should happen only in unreachable
|
||||
parts of code, or for invalid programs. */
|
||||
if (compare_values (min, max) == 1)
|
||||
return;
|
||||
|
||||
@ -2072,8 +2083,8 @@ adjust_range_with_scev (value_range_t *vr, struct loop *loop, tree stmt,
|
||||
}
|
||||
else if (vr->type == VR_RANGE)
|
||||
{
|
||||
tree min = vr->min;
|
||||
tree max = vr->max;
|
||||
min = vr->min;
|
||||
max = vr->max;
|
||||
|
||||
if (dir == EV_DIR_DECREASES)
|
||||
{
|
||||
@ -2084,10 +2095,11 @@ adjust_range_with_scev (value_range_t *vr, struct loop *loop, tree stmt,
|
||||
max = init;
|
||||
|
||||
/* If we just created an invalid range with the minimum
|
||||
greater than the maximum, take the minimum all the
|
||||
way to -INF. */
|
||||
greater than the maximum, we fail conservatively.
|
||||
This should happen only in unreachable
|
||||
parts of code, or for invalid programs. */
|
||||
if (compare_values (min, max) == 1)
|
||||
min = TYPE_MIN_VALUE (TREE_TYPE (min));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -2097,11 +2109,9 @@ adjust_range_with_scev (value_range_t *vr, struct loop *loop, tree stmt,
|
||||
{
|
||||
min = init;
|
||||
|
||||
/* If we just created an invalid range with the minimum
|
||||
greater than the maximum, take the maximum all the
|
||||
way to +INF. */
|
||||
/* Again, avoid creating invalid range by failing. */
|
||||
if (compare_values (min, max) == 1)
|
||||
max = TYPE_MAX_VALUE (TREE_TYPE (max));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user