Makefile.in (tree-data-ref.o): Add langhooks.h dependency.

* Makefile.in (tree-data-ref.o): Add langhooks.h dependency.
	* tree-ssa-loop-niter.c (derive_constant_upper_bound):  Follow
	ud-chains.  Handle AND_EXPR.
	(record_estimate): Record whether the estimate is realistic
	and whether it is derived from a loop exit.
	(record_nonwrapping_iv, idx_infer_loop_bounds, infer_loop_bounds_from_ref,
	infer_loop_bounds_from_array, infer_loop_bounds_from_signedness): New
	functions.
	(compute_estimated_nb_iterations): Take only realistic bounds into
	account.  Set estimate_state.  Use double_ints.
	(infer_loop_bounds_from_undefined): Call infer_loop_bounds_from_array
	and infer_loop_bounds_from_signedness.  Do not consider basic blocks
	that do not have to be always executed.
	(estimate_numbers_of_iterations_loop): Set estimate_state, and use it
	to determine whether to call infer_loop_bounds_from_undefined
	and compute_estimated_nb_iterations.
	(n_of_executions_at_most): Use double_ints.
	(free_numbers_of_iterations_estimates_loop): Set estimate_state.
	(substitute_in_loop_info): Do not replace in estimated_nb_iterations.
	* double-int.c (double_int_to_tree): Improve comment.
	(double_int_fits_to_tree_p): New function.
	* double-int.h (double_int_fits_to_tree_p): Declare.
	* tree-data-ref.c: Include langhooks.h.
	(estimate_niter_from_size_of_data, estimate_iters_using_array): Removed.
	(analyze_array_indexes): Do not call estimate_niter_from_size_of_data.
	(analyze_array): Do not pass estimate_only argument to
	analyze_array_indexes.
	(get_number_of_iters_for_loop): Build tree from the stored double_int
	value.
	(get_references_in_stmt, find_data_references_in_stmt): New functions.
	(find_data_references_in_loop): Use find_data_references_in_stmt.
	* tree-data-ref.h (struct data_ref_loc_d): New.
	(get_references_in_stmt): Declare.
	(estimate_iters_using_array): Declaration removed.
	* cfgloop.h (struct nb_iter_bound): Change type of bound to
	double_int.  Improve comments.  Add is_exit and realistic
	fields.
	(struct loop): Changed type of estimated_nb_iterations to double_int.
	Added estimate_state field.
	(record_estimate): Declaration removed.

From-SVN: r118729
This commit is contained in:
Zdenek Dvorak 2006-11-12 20:58:05 +01:00 committed by Zdenek Dvorak
parent 47eb5b329b
commit 946e1bc757
8 changed files with 577 additions and 336 deletions

View File

@ -1,3 +1,46 @@
2006-11-12 Zdenek Dvorak <dvorakz@suse.cz>
* Makefile.in (tree-data-ref.o): Add langhooks.h dependency.
* tree-ssa-loop-niter.c (derive_constant_upper_bound): Follow
ud-chains. Handle AND_EXPR.
(record_estimate): Record whether the estimate is realistic
and whether it is derived from a loop exit.
(record_nonwrapping_iv, idx_infer_loop_bounds, infer_loop_bounds_from_ref,
infer_loop_bounds_from_array, infer_loop_bounds_from_signedness): New
functions.
(compute_estimated_nb_iterations): Take only realistic bounds into
account. Set estimate_state. Use double_ints.
(infer_loop_bounds_from_undefined): Call infer_loop_bounds_from_array
and infer_loop_bounds_from_signedness. Do not consider basic blocks
that do not have to be always executed.
(estimate_numbers_of_iterations_loop): Set estimate_state, and use it
to determine whether to call infer_loop_bounds_from_undefined
and compute_estimated_nb_iterations.
(n_of_executions_at_most): Use double_ints.
(free_numbers_of_iterations_estimates_loop): Set estimate_state.
(substitute_in_loop_info): Do not replace in estimated_nb_iterations.
* double-int.c (double_int_to_tree): Improve comment.
(double_int_fits_to_tree_p): New function.
* double-int.h (double_int_fits_to_tree_p): Declare.
* tree-data-ref.c: Include langhooks.h.
(estimate_niter_from_size_of_data, estimate_iters_using_array): Removed.
(analyze_array_indexes): Do not call estimate_niter_from_size_of_data.
(analyze_array): Do not pass estimate_only argument to
analyze_array_indexes.
(get_number_of_iters_for_loop): Build tree from the stored double_int
value.
(get_references_in_stmt, find_data_references_in_stmt): New functions.
(find_data_references_in_loop): Use find_data_references_in_stmt.
* tree-data-ref.h (struct data_ref_loc_d): New.
(get_references_in_stmt): Declare.
(estimate_iters_using_array): Declaration removed.
* cfgloop.h (struct nb_iter_bound): Change type of bound to
double_int. Improve comments. Add is_exit and realistic
fields.
(struct loop): Changed type of estimated_nb_iterations to double_int.
Added estimate_state field.
(record_estimate): Declaration removed.
2006-11-12 Zdenek Dvorak <dvorakz@suse.cz>
* params.c (set_param_value): Initialize the "set" field.

View File

@ -2080,7 +2080,7 @@ tree-scalar-evolution.o: tree-scalar-evolution.c $(CONFIG_H) $(SYSTEM_H) \
tree-data-ref.o: tree-data-ref.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(GGC_H) $(TREE_H) $(RTL_H) $(BASIC_BLOCK_H) $(DIAGNOSTIC_H) \
$(TREE_FLOW_H) $(TREE_DUMP_H) $(TIMEVAR_H) $(CFGLOOP_H) \
$(TREE_DATA_REF_H) $(SCEV_H) tree-pass.h tree-chrec.h
$(TREE_DATA_REF_H) $(SCEV_H) tree-pass.h tree-chrec.h langhooks.h
tree-vect-analyze.o: tree-vect-analyze.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TM_H) $(GGC_H) $(OPTABS_H) $(TREE_H) $(BASIC_BLOCK_H) \
$(DIAGNOSTIC_H) $(TREE_FLOW_H) $(TREE_DUMP_H) $(TIMEVAR_H) $(CFGLOOP_H) \

View File

@ -47,12 +47,31 @@ struct lpt_decision
struct nb_iter_bound
{
tree bound; /* The constant expression whose value is an upper
bound on the number of executions of ... */
tree at_stmt; /* ... this statement during one execution of
a loop. */
/* The statement STMT is executed at most ... */
tree stmt;
/* ... BOUND + 1 times (BOUND must be an unsigned constant).
The + 1 is added for the following reasons:
a) 0 would otherwise be unused, while we would need to care more about
overflows (as MAX + 1 is sometimes produced as the estimate on number
of executions of STMT).
b) it is consistent with the result of number_of_iterations_exit. */
double_int bound;
/* True if the statement will cause the loop to be leaved the (at most)
BOUND + 1-st time it is executed, that is, all the statements after it
are executed at most BOUND times. */
bool is_exit;
/* True if the bound is "realistic" -- i.e., most likely the loop really has
number of iterations close to the bound. Exact bounds (if the number of
iterations of a loop is a constant) and bounds derived from the size of
data accessed in the loop are considered realistic. */
bool realistic;
/* The next bound in the list. */
struct nb_iter_bound *next;
/* The next bound in a list. */
};
/* Structure to hold information for each natural loop. */
@ -111,9 +130,18 @@ struct loop
information in this field. */
tree nb_iterations;
/* An INTEGER_CST estimation of the number of iterations. NULL_TREE
if there is no estimation. */
tree estimated_nb_iterations;
/* An integer estimation of the number of iterations. Estimate_state
describes what is the state of the estimation. */
enum
{
/* Estimate was not computed yet. */
EST_NOT_COMPUTED,
/* Estimate was computed, but we could derive no useful bound. */
EST_NOT_AVAILABLE,
/* Estimate is ready. */
EST_AVAILABLE
} estimate_state;
double_int estimated_nb_iterations;
/* Upper bound on number of iterations of a loop. */
struct nb_iter_bound *bounds;
@ -398,6 +426,5 @@ enum
extern void unroll_and_peel_loops (struct loops *, int);
extern void doloop_optimize_loops (struct loops *);
extern void move_loop_invariants (struct loops *);
extern void record_estimate (struct loop *, tree, tree, tree);
#endif /* GCC_CFGLOOP_H */

View File

@ -290,7 +290,8 @@ double_int_umod (double_int a, double_int b, unsigned code)
return double_int_mod (a, b, true, code);
}
/* Constructs tree in type TYPE from with value given by CST. */
/* Constructs tree in type TYPE from with value given by CST. Signedness of CST
is assumed to be the same as the signedness of TYPE. */
tree
double_int_to_tree (tree type, double_int cst)
@ -300,6 +301,19 @@ double_int_to_tree (tree type, double_int cst)
return build_int_cst_wide (type, cst.low, cst.high);
}
/* Returns true if CST fits into range of TYPE. Signedness of CST is assumed
to be the same as the signedness of TYPE. */
bool
double_int_fits_to_tree_p (tree type, double_int cst)
{
double_int ext = double_int_ext (cst,
TYPE_PRECISION (type),
TYPE_UNSIGNED (type));
return double_int_equal_p (cst, ext);
}
/* Returns true if CST is negative. Of course, CST is considered to
be signed. */

View File

@ -58,7 +58,8 @@ union tree_node;
/* Constructors and conversions. */
union tree_node *double_int_to_tree (union tree_node *, double_int);
double_int tree_to_double_int (union tree_node *tree);
bool double_int_fits_to_tree_p (union tree_node *, double_int);
double_int tree_to_double_int (union tree_node *);
/* Constructs double_int from integer CST. The bits over the precision of
HOST_WIDE_INT are filled with the sign bit. */

View File

@ -93,6 +93,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-pass.h"
#include "langhooks.h"
static struct datadep_stats
{
@ -888,75 +889,16 @@ dump_ddrs (FILE *file, VEC (ddr_p, heap) *ddrs)
/* Estimate the number of iterations from the size of the data and the
access functions. */
static void
estimate_niter_from_size_of_data (struct loop *loop,
tree opnd0,
tree access_fn,
tree stmt)
{
tree estimation = NULL_TREE;
tree array_size, data_size, element_size;
tree init, step;
init = initial_condition (access_fn);
step = evolution_part_in_loop_num (access_fn, loop->num);
array_size = TYPE_SIZE (TREE_TYPE (opnd0));
element_size = TYPE_SIZE (TREE_TYPE (TREE_TYPE (opnd0)));
if (array_size == NULL_TREE
|| TREE_CODE (array_size) != INTEGER_CST
|| TREE_CODE (element_size) != INTEGER_CST)
return;
data_size = fold_build2 (EXACT_DIV_EXPR, integer_type_node,
array_size, element_size);
if (init != NULL_TREE
&& step != NULL_TREE
&& TREE_CODE (init) == INTEGER_CST
&& TREE_CODE (step) == INTEGER_CST)
{
tree i_plus_s = fold_build2 (PLUS_EXPR, integer_type_node, init, step);
tree sign = fold_binary (GT_EXPR, boolean_type_node, i_plus_s, init);
if (sign == boolean_true_node)
estimation = fold_build2 (CEIL_DIV_EXPR, integer_type_node,
fold_build2 (MINUS_EXPR, integer_type_node,
data_size, init), step);
/* When the step is negative, as in PR23386: (init = 3, step =
0ffffffff, data_size = 100), we have to compute the
estimation as ceil_div (init, 0 - step) + 1. */
else if (sign == boolean_false_node)
estimation =
fold_build2 (PLUS_EXPR, integer_type_node,
fold_build2 (CEIL_DIV_EXPR, integer_type_node,
init,
fold_build2 (MINUS_EXPR, unsigned_type_node,
integer_zero_node, step)),
integer_one_node);
if (estimation)
record_estimate (loop, estimation, boolean_true_node, stmt);
}
}
/* Given an ARRAY_REF node REF, records its access functions.
Example: given A[i][3], record in ACCESS_FNS the opnd1 function,
i.e. the constant "3", then recursively call the function on opnd0,
i.e. the ARRAY_REF "A[i]".
If ESTIMATE_ONLY is true, we just set the estimated number of loop
iterations, we don't store the access function.
The function returns the base name: "A". */
static tree
analyze_array_indexes (struct loop *loop,
VEC(tree,heap) **access_fns,
tree ref, tree stmt,
bool estimate_only)
tree ref, tree stmt)
{
tree opnd0, opnd1;
tree access_fn;
@ -971,32 +913,17 @@ analyze_array_indexes (struct loop *loop,
access_fn = instantiate_parameters
(loop, analyze_scalar_evolution (loop, opnd1));
if (estimate_only
&& chrec_contains_undetermined (loop->estimated_nb_iterations))
estimate_niter_from_size_of_data (loop, opnd0, access_fn, stmt);
if (!estimate_only)
VEC_safe_push (tree, heap, *access_fns, access_fn);
VEC_safe_push (tree, heap, *access_fns, access_fn);
/* Recursively record other array access functions. */
if (TREE_CODE (opnd0) == ARRAY_REF)
return analyze_array_indexes (loop, access_fns, opnd0, stmt, estimate_only);
return analyze_array_indexes (loop, access_fns, opnd0, stmt);
/* Return the base name of the data access. */
else
return opnd0;
}
/* For an array reference REF contained in STMT, attempt to bound the
number of iterations in the loop containing STMT */
void
estimate_iters_using_array (tree stmt, tree ref)
{
analyze_array_indexes (loop_containing_stmt (stmt), NULL, ref, stmt,
true);
}
/* For a data reference REF contained in the statement STMT, initialize
a DATA_REFERENCE structure, and return it. IS_READ flag has to be
set to true when REF is in the right hand side of an
@ -1022,7 +949,7 @@ analyze_array (tree stmt, tree ref, bool is_read)
DR_REF (res) = ref;
acc_fns = VEC_alloc (tree, heap, 3);
DR_BASE_OBJECT (res) = analyze_array_indexes
(loop_containing_stmt (stmt), &acc_fns, ref, stmt, false);
(loop_containing_stmt (stmt), &acc_fns, ref, stmt);
DR_TYPE (res) = ARRAY_REF_TYPE;
DR_SET_ACCESS_FNS (res, acc_fns);
DR_IS_READ (res) = is_read;
@ -2377,13 +2304,20 @@ analyze_ziv_subscript (tree chrec_a,
static tree
get_number_of_iters_for_loop (int loopnum)
{
tree numiter = number_of_iterations_in_loop (current_loops->parray[loopnum]);
struct loop *loop = current_loops->parray[loopnum];
tree numiter = number_of_iterations_in_loop (loop);
if (TREE_CODE (numiter) != INTEGER_CST)
numiter = current_loops->parray[loopnum]->estimated_nb_iterations;
if (chrec_contains_undetermined (numiter))
return NULL_TREE;
return numiter;
if (TREE_CODE (numiter) == INTEGER_CST)
return numiter;
if (loop->estimate_state == EST_AVAILABLE)
{
tree type = lang_hooks.types.type_for_size (INT_TYPE_SIZE, true);
if (double_int_fits_to_tree_p (type, loop->estimated_nb_iterations))
return double_int_to_tree (type, loop->estimated_nb_iterations);
}
return NULL_TREE;
}
/* Analyze a SIV (Single Index Variable) subscript where CHREC_A is a
@ -4060,6 +3994,105 @@ compute_all_dependences (VEC (data_reference_p, heap) *datarefs,
}
}
/* Stores the locations of memory references in STMT to REFERENCES. Returns
true if STMT clobbers memory, false otherwise. */
bool
get_references_in_stmt (tree stmt, VEC (data_ref_loc, heap) **references)
{
bool clobbers_memory = false;
data_ref_loc *ref;
tree *op0, *op1, args, call;
*references = NULL;
/* ASM_EXPR and CALL_EXPR may embed arbitrary side effects.
Calls have side-effects, except those to const or pure
functions. */
call = get_call_expr_in (stmt);
if ((call
&& !(call_expr_flags (call) & (ECF_CONST | ECF_PURE)))
|| (TREE_CODE (stmt) == ASM_EXPR
&& ASM_VOLATILE_P (stmt)))
clobbers_memory = true;
if (ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS))
return clobbers_memory;
if (TREE_CODE (stmt) == MODIFY_EXPR)
{
op0 = &TREE_OPERAND (stmt, 0);
op1 = &TREE_OPERAND (stmt, 1);
if (DECL_P (*op1)
|| REFERENCE_CLASS_P (*op1))
{
ref = VEC_safe_push (data_ref_loc, heap, *references, NULL);
ref->pos = op1;
ref->is_read = true;
}
if (DECL_P (*op0)
|| REFERENCE_CLASS_P (*op0))
{
ref = VEC_safe_push (data_ref_loc, heap, *references, NULL);
ref->pos = op0;
ref->is_read = false;
}
}
if (call)
{
for (args = TREE_OPERAND (call, 1); args; args = TREE_CHAIN (args))
{
op0 = &TREE_VALUE (args);
if (DECL_P (*op0)
|| REFERENCE_CLASS_P (*op0))
{
ref = VEC_safe_push (data_ref_loc, heap, *references, NULL);
ref->pos = op0;
ref->is_read = true;
}
}
}
return clobbers_memory;
}
/* Stores the data references in STMT to DATAREFS. If there is an unanalyzable
reference, returns false, otherwise returns true. */
static bool
find_data_references_in_stmt (tree stmt,
VEC (data_reference_p, heap) **datarefs)
{
unsigned i;
VEC (data_ref_loc, heap) *references;
data_ref_loc *ref;
bool ret = true;
data_reference_p dr;
if (get_references_in_stmt (stmt, &references))
{
VEC_free (data_ref_loc, heap, references);
return false;
}
for (i = 0; VEC_iterate (data_ref_loc, references, i, ref); i++)
{
dr = create_data_ref (*ref->pos, stmt, ref->is_read);
if (dr)
VEC_safe_push (data_reference_p, heap, *datarefs, dr);
else
{
ret = false;
break;
}
}
VEC_free (data_ref_loc, heap, references);
return ret;
}
/* Search the data references in LOOP, and record the information into
DATAREFS. Returns chrec_dont_know when failing to analyze a
difficult case, returns NULL_TREE otherwise.
@ -4074,118 +4107,41 @@ find_data_references_in_loop (struct loop *loop,
basic_block bb, *bbs;
unsigned int i;
block_stmt_iterator bsi;
struct data_reference *dr;
bbs = get_loop_body (loop);
loop->parallel_p = true;
for (i = 0; i < loop->num_nodes; i++)
{
bb = bbs[i];
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
{
tree stmt = bsi_stmt (bsi);
/* ASM_EXPR and CALL_EXPR may embed arbitrary side effects.
Calls have side-effects, except those to const or pure
functions. */
if ((TREE_CODE (stmt) == CALL_EXPR
&& !(call_expr_flags (stmt) & (ECF_CONST | ECF_PURE)))
|| (TREE_CODE (stmt) == ASM_EXPR
&& ASM_VOLATILE_P (stmt)))
goto insert_dont_know_node;
if (ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS))
continue;
switch (TREE_CODE (stmt))
if (!find_data_references_in_stmt (stmt, datarefs))
{
case MODIFY_EXPR:
{
bool one_inserted = false;
tree opnd0 = TREE_OPERAND (stmt, 0);
tree opnd1 = TREE_OPERAND (stmt, 1);
if (TREE_CODE (opnd0) == ARRAY_REF
|| TREE_CODE (opnd0) == INDIRECT_REF
|| TREE_CODE (opnd0) == COMPONENT_REF)
{
dr = create_data_ref (opnd0, stmt, false);
if (dr)
{
VEC_safe_push (data_reference_p, heap, *datarefs, dr);
one_inserted = true;
}
}
struct data_reference *res;
res = XNEW (struct data_reference);
DR_STMT (res) = NULL_TREE;
DR_REF (res) = NULL_TREE;
DR_BASE_OBJECT (res) = NULL;
DR_TYPE (res) = ARRAY_REF_TYPE;
DR_SET_ACCESS_FNS (res, NULL);
DR_BASE_OBJECT (res) = NULL;
DR_IS_READ (res) = false;
DR_BASE_ADDRESS (res) = NULL_TREE;
DR_OFFSET (res) = NULL_TREE;
DR_INIT (res) = NULL_TREE;
DR_STEP (res) = NULL_TREE;
DR_OFFSET_MISALIGNMENT (res) = NULL_TREE;
DR_MEMTAG (res) = NULL_TREE;
DR_PTR_INFO (res) = NULL;
loop->parallel_p = false;
VEC_safe_push (data_reference_p, heap, *datarefs, res);
if (TREE_CODE (opnd1) == ARRAY_REF
|| TREE_CODE (opnd1) == INDIRECT_REF
|| TREE_CODE (opnd1) == COMPONENT_REF)
{
dr = create_data_ref (opnd1, stmt, true);
if (dr)
{
VEC_safe_push (data_reference_p, heap, *datarefs, dr);
one_inserted = true;
}
}
if (!one_inserted)
goto insert_dont_know_node;
break;
}
case CALL_EXPR:
{
tree args;
bool one_inserted = false;
for (args = TREE_OPERAND (stmt, 1); args;
args = TREE_CHAIN (args))
if (TREE_CODE (TREE_VALUE (args)) == ARRAY_REF
|| TREE_CODE (TREE_VALUE (args)) == INDIRECT_REF
|| TREE_CODE (TREE_VALUE (args)) == COMPONENT_REF)
{
dr = create_data_ref (TREE_VALUE (args), stmt, true);
if (dr)
{
VEC_safe_push (data_reference_p, heap, *datarefs, dr);
one_inserted = true;
}
}
if (!one_inserted)
goto insert_dont_know_node;
break;
}
default:
{
struct data_reference *res;
insert_dont_know_node:;
res = XNEW (struct data_reference);
DR_STMT (res) = NULL_TREE;
DR_REF (res) = NULL_TREE;
DR_BASE_OBJECT (res) = NULL;
DR_TYPE (res) = ARRAY_REF_TYPE;
DR_SET_ACCESS_FNS (res, NULL);
DR_BASE_OBJECT (res) = NULL;
DR_IS_READ (res) = false;
DR_BASE_ADDRESS (res) = NULL_TREE;
DR_OFFSET (res) = NULL_TREE;
DR_INIT (res) = NULL_TREE;
DR_STEP (res) = NULL_TREE;
DR_OFFSET_MISALIGNMENT (res) = NULL_TREE;
DR_MEMTAG (res) = NULL_TREE;
DR_PTR_INFO (res) = NULL;
VEC_safe_push (data_reference_p, heap, *datarefs, res);
free (bbs);
return chrec_dont_know;
}
free (bbs);
return chrec_dont_know;
}
/* When there are no defs in the loop, the loop is parallel. */
@ -4193,7 +4149,6 @@ find_data_references_in_loop (struct loop *loop,
loop->parallel_p = false;
}
}
free (bbs);
return NULL_TREE;

View File

@ -269,6 +269,21 @@ DEF_VEC_ALLOC_P(ddr_p,heap);
/* Describes a location of a memory reference. */
typedef struct data_ref_loc_d
{
/* Position of the memory reference. */
tree *pos;
/* True if the memory reference is read. */
bool is_read;
} data_ref_loc;
DEF_VEC_O (data_ref_loc);
DEF_VEC_ALLOC_O (data_ref_loc, heap);
bool get_references_in_stmt (tree, VEC (data_ref_loc, heap) **);
extern tree find_data_references_in_loop (struct loop *,
VEC (data_reference_p, heap) **);
extern void compute_data_dependences_for_loop (struct loop *, bool,
@ -292,7 +307,6 @@ extern void free_dependence_relation (struct data_dependence_relation *);
extern void free_dependence_relations (VEC (ddr_p, heap) *);
extern void free_data_refs (VEC (data_reference_p, heap) *);
extern struct data_reference *analyze_array (tree, tree, bool);
extern void estimate_iters_using_array (tree, tree);
/* Return the index of the variable VAR in the LOOP_NEST array. */

View File

@ -1533,6 +1533,7 @@ derive_constant_upper_bound (tree val, tree additional)
tree type = TREE_TYPE (val);
tree op0, op1, subtype, maxt;
double_int bnd, max, mmax, cst;
tree stmt;
if (INTEGRAL_TYPE_P (type))
maxt = TYPE_MAX_VALUE (type);
@ -1647,39 +1648,113 @@ derive_constant_upper_bound (tree val, tree additional)
bnd = derive_constant_upper_bound (op0, additional);
return double_int_udiv (bnd, tree_to_double_int (op1), FLOOR_DIV_EXPR);
case BIT_AND_EXPR:
op1 = TREE_OPERAND (val, 1);
if (TREE_CODE (op1) != INTEGER_CST
|| tree_int_cst_sign_bit (op1))
return max;
return tree_to_double_int (op1);
case SSA_NAME:
stmt = SSA_NAME_DEF_STMT (val);
if (TREE_CODE (stmt) != MODIFY_EXPR
|| TREE_OPERAND (stmt, 0) != val)
return max;
return derive_constant_upper_bound (TREE_OPERAND (stmt, 1), additional);
default:
return max;
}
}
/* Records that AT_STMT is executed at most BOUND times in LOOP. The
additional condition ADDITIONAL is recorded with the bound. */
/* Records that AT_STMT is executed at most BOUND + 1 times in LOOP. The
additional condition ADDITIONAL is recorded with the bound. IS_EXIT
is true if the loop is exited immediately after STMT, and this exit
is taken at last when the STMT is executed BOUND + 1 times.
REALISTIC is true if the estimate comes from a reliable source
(number of iterations analysis, or size of data accessed in the loop). */
void
record_estimate (struct loop *loop, tree bound, tree additional, tree at_stmt)
static void
record_estimate (struct loop *loop, tree bound, tree additional, tree at_stmt,
bool is_exit, bool realistic)
{
struct nb_iter_bound *elt = xmalloc (sizeof (struct nb_iter_bound));
double_int i_bound = derive_constant_upper_bound (bound, additional);
tree c_bound = double_int_to_tree (unsigned_type_for (TREE_TYPE (bound)),
i_bound);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Statements after ");
fprintf (dump_file, "Statement %s", is_exit ? "(exit)" : "");
print_generic_expr (dump_file, at_stmt, TDF_SLIM);
fprintf (dump_file, " are executed at most ");
fprintf (dump_file, " is executed at most ");
print_generic_expr (dump_file, bound, TDF_SLIM);
fprintf (dump_file, " (bounded by ");
print_generic_expr (dump_file, c_bound, TDF_SLIM);
fprintf (dump_file, ") times in loop %d.\n", loop->num);
dump_double_int (dump_file, i_bound, true);
fprintf (dump_file, ") + 1 times in loop %d.\n", loop->num);
}
elt->bound = c_bound;
elt->at_stmt = at_stmt;
elt->bound = i_bound;
elt->stmt = at_stmt;
elt->is_exit = is_exit;
elt->realistic = realistic && TREE_CODE (bound) == INTEGER_CST;
elt->next = loop->bounds;
loop->bounds = elt;
}
/* Record the estimate on number of iterations of LOOP based on the fact that
the induction variable BASE + STEP * i evaluated in STMT does not wrap and
its values belong to the range <LOW, HIGH>. DATA_SIZE_BOUNDS_P is true if
LOW and HIGH are derived from the size of data. */
static void
record_nonwrapping_iv (struct loop *loop, tree base, tree step, tree stmt,
tree low, tree high, bool data_size_bounds_p)
{
tree niter_bound, extreme, delta;
tree type = TREE_TYPE (base), unsigned_type;
if (TREE_CODE (step) != INTEGER_CST || zero_p (step))
return;
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Induction variable (");
print_generic_expr (dump_file, TREE_TYPE (base), TDF_SLIM);
fprintf (dump_file, ") ");
print_generic_expr (dump_file, base, TDF_SLIM);
fprintf (dump_file, " + ");
print_generic_expr (dump_file, step, TDF_SLIM);
fprintf (dump_file, " * iteration does not wrap in statement ");
print_generic_expr (dump_file, stmt, TDF_SLIM);
fprintf (dump_file, " in loop %d.\n", loop->num);
}
unsigned_type = unsigned_type_for (type);
base = fold_convert (unsigned_type, base);
step = fold_convert (unsigned_type, step);
if (tree_int_cst_sign_bit (step))
{
extreme = fold_convert (unsigned_type, low);
if (TREE_CODE (base) != INTEGER_CST)
base = fold_convert (unsigned_type, high);
delta = fold_build2 (MINUS_EXPR, unsigned_type, base, extreme);
step = fold_build1 (NEGATE_EXPR, unsigned_type, step);
}
else
{
extreme = fold_convert (unsigned_type, high);
if (TREE_CODE (base) != INTEGER_CST)
base = fold_convert (unsigned_type, low);
delta = fold_build2 (MINUS_EXPR, unsigned_type, extreme, base);
}
/* STMT is executed at most NITER_BOUND + 1 times, since otherwise the value
would get out of the range. */
niter_bound = fold_build2 (FLOOR_DIV_EXPR, unsigned_type, delta, step);
record_estimate (loop, niter_bound, boolean_true_node, stmt,
false, data_size_bounds_p);
}
/* Initialize LOOP->ESTIMATED_NB_ITERATIONS with the lowest safe
approximation of the number of iterations for LOOP. */
@ -1687,20 +1762,184 @@ static void
compute_estimated_nb_iterations (struct loop *loop)
{
struct nb_iter_bound *bound;
gcc_assert (loop->estimate_state == EST_NOT_AVAILABLE);
for (bound = loop->bounds; bound; bound = bound->next)
{
if (TREE_CODE (bound->bound) != INTEGER_CST)
if (!bound->realistic)
continue;
/* Update only when there is no previous estimation, or when the current
estimation is smaller. */
if (chrec_contains_undetermined (loop->estimated_nb_iterations)
|| tree_int_cst_lt (bound->bound, loop->estimated_nb_iterations))
loop->estimated_nb_iterations = bound->bound;
if (loop->estimate_state == EST_NOT_AVAILABLE
|| double_int_ucmp (bound->bound, loop->estimated_nb_iterations) < 0)
{
loop->estimate_state = EST_AVAILABLE;
loop->estimated_nb_iterations = bound->bound;
}
}
}
/* Determine information about number of iterations a LOOP from the index
IDX of a data reference accessed in STMT. Callback for for_each_index. */
struct ilb_data
{
struct loop *loop;
tree stmt;
};
static bool
idx_infer_loop_bounds (tree base, tree *idx, void *dta)
{
struct ilb_data *data = dta;
tree ev, init, step;
tree low, high, type, next;
bool sign;
struct loop *loop = data->loop;
if (TREE_CODE (base) != ARRAY_REF)
return true;
ev = instantiate_parameters (loop, analyze_scalar_evolution (loop, *idx));
init = initial_condition (ev);
step = evolution_part_in_loop_num (ev, loop->num);
if (!init
|| !step
|| TREE_CODE (step) != INTEGER_CST
|| zero_p (step)
|| tree_contains_chrecs (init, NULL)
|| chrec_contains_symbols_defined_in_loop (init, loop->num))
return true;
low = array_ref_low_bound (base);
high = array_ref_up_bound (base);
/* The case of nonconstant bounds could be handled, but it would be
complicated. */
if (TREE_CODE (low) != INTEGER_CST
|| !high
|| TREE_CODE (high) != INTEGER_CST)
return true;
sign = tree_int_cst_sign_bit (step);
type = TREE_TYPE (step);
/* In case the relevant bound of the array does not fit in type, or
it does, but bound + step (in type) still belongs into the range of the
array, the index may wrap and still stay within the range of the array
(consider e.g. if the array is indexed by the full range of
unsigned char).
To make things simpler, we require both bounds to fit into type, although
there are cases where this would not be strightly necessary. */
if (!int_fits_type_p (high, type)
|| !int_fits_type_p (low, type))
return true;
low = fold_convert (type, low);
high = fold_convert (type, high);
if (sign)
next = fold_binary (PLUS_EXPR, type, low, step);
else
next = fold_binary (PLUS_EXPR, type, high, step);
if (tree_int_cst_compare (low, next) <= 0
&& tree_int_cst_compare (next, high) <= 0)
return true;
record_nonwrapping_iv (loop, init, step, data->stmt, low, high, true);
return true;
}
/* Determine information about number of iterations a LOOP from the bounds
of arrays in the data reference REF accessed in STMT. */
static void
infer_loop_bounds_from_ref (struct loop *loop, tree stmt, tree ref)
{
struct ilb_data data;
data.loop = loop;
data.stmt = stmt;
for_each_index (&ref, idx_infer_loop_bounds, &data);
}
/* Determine information about number of iterations of a LOOP from the way
arrays are used in STMT. */
static void
infer_loop_bounds_from_array (struct loop *loop, tree stmt)
{
tree call;
if (TREE_CODE (stmt) == MODIFY_EXPR)
{
tree op0 = TREE_OPERAND (stmt, 0);
tree op1 = TREE_OPERAND (stmt, 1);
/* For each memory access, analyze its access function
and record a bound on the loop iteration domain. */
if (REFERENCE_CLASS_P (op0))
infer_loop_bounds_from_ref (loop, stmt, op0);
if (REFERENCE_CLASS_P (op1))
infer_loop_bounds_from_ref (loop, stmt, op1);
}
call = get_call_expr_in (stmt);
if (call)
{
tree args;
for (args = TREE_OPERAND (call, 1); args; args = TREE_CHAIN (args))
if (REFERENCE_CLASS_P (TREE_VALUE (args)))
infer_loop_bounds_from_ref (loop, stmt, TREE_VALUE (args));
}
}
/* Determine information about number of iterations of a LOOP from the fact
that signed arithmetics in STMT does not overflow. */
static void
infer_loop_bounds_from_signedness (struct loop *loop, tree stmt)
{
tree def, base, step, scev, type, low, high;
if (flag_wrapv || TREE_CODE (stmt) != MODIFY_EXPR)
return;
def = TREE_OPERAND (stmt, 0);
if (TREE_CODE (def) != SSA_NAME)
return;
type = TREE_TYPE (def);
if (!INTEGRAL_TYPE_P (type)
|| TYPE_UNSIGNED (type))
return;
scev = instantiate_parameters (loop, analyze_scalar_evolution (loop, def));
if (chrec_contains_undetermined (scev))
return;
base = initial_condition_in_loop_num (scev, loop->num);
step = evolution_part_in_loop_num (scev, loop->num);
if (!base || !step
|| TREE_CODE (step) != INTEGER_CST
|| tree_contains_chrecs (base, NULL)
|| chrec_contains_symbols_defined_in_loop (base, loop->num))
return;
low = lower_bound_in_type (type, type);
high = upper_bound_in_type (type, type);
record_nonwrapping_iv (loop, base, step, stmt, low, high, false);
}
/* The following analyzers are extracting informations on the bounds
of LOOP from the following undefined behaviors:
@ -1714,8 +1953,9 @@ static void
infer_loop_bounds_from_undefined (struct loop *loop)
{
unsigned i;
basic_block bb, *bbs;
basic_block *bbs;
block_stmt_iterator bsi;
basic_block bb;
bbs = get_loop_body (loop);
@ -1723,95 +1963,21 @@ infer_loop_bounds_from_undefined (struct loop *loop)
{
bb = bbs[i];
/* If BB is not executed in each iteration of the loop, we cannot
use it to infer any information about # of iterations of the loop. */
if (!dominated_by_p (CDI_DOMINATORS, loop->latch, bb))
continue;
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
{
tree stmt = bsi_stmt (bsi);
switch (TREE_CODE (stmt))
{
case MODIFY_EXPR:
{
tree op0 = TREE_OPERAND (stmt, 0);
tree op1 = TREE_OPERAND (stmt, 1);
infer_loop_bounds_from_array (loop, stmt);
infer_loop_bounds_from_signedness (loop, stmt);
}
/* For each array access, analyze its access function
and record a bound on the loop iteration domain. */
if (TREE_CODE (op1) == ARRAY_REF
&& !array_ref_contains_indirect_ref (op1))
estimate_iters_using_array (stmt, op1);
if (TREE_CODE (op0) == ARRAY_REF
&& !array_ref_contains_indirect_ref (op0))
estimate_iters_using_array (stmt, op0);
/* For each signed type variable in LOOP, analyze its
scalar evolution and record a bound of the loop
based on the type's ranges. */
else if (!flag_wrapv && TREE_CODE (op0) == SSA_NAME)
{
tree init, step, diff, estimation;
tree scev = instantiate_parameters
(loop, analyze_scalar_evolution (loop, op0));
tree type = chrec_type (scev);
if (chrec_contains_undetermined (scev)
|| TYPE_UNSIGNED (type))
break;
init = initial_condition_in_loop_num (scev, loop->num);
step = evolution_part_in_loop_num (scev, loop->num);
if (init == NULL_TREE
|| step == NULL_TREE
|| TREE_CODE (init) != INTEGER_CST
|| TREE_CODE (step) != INTEGER_CST
|| TYPE_MIN_VALUE (type) == NULL_TREE
|| TYPE_MAX_VALUE (type) == NULL_TREE)
break;
if (integer_nonzerop (step))
{
tree utype;
if (tree_int_cst_lt (step, integer_zero_node))
diff = fold_build2 (MINUS_EXPR, type, init,
TYPE_MIN_VALUE (type));
else
diff = fold_build2 (MINUS_EXPR, type,
TYPE_MAX_VALUE (type), init);
utype = unsigned_type_for (type);
estimation = fold_build2 (CEIL_DIV_EXPR, type, diff,
step);
record_estimate (loop,
fold_convert (utype, estimation),
boolean_true_node, stmt);
}
}
break;
}
case CALL_EXPR:
{
tree args;
for (args = TREE_OPERAND (stmt, 1); args;
args = TREE_CHAIN (args))
if (TREE_CODE (TREE_VALUE (args)) == ARRAY_REF
&& !array_ref_contains_indirect_ref (TREE_VALUE (args)))
estimate_iters_using_array (stmt, TREE_VALUE (args));
break;
}
default:
break;
}
}
}
compute_estimated_nb_iterations (loop);
free (bbs);
}
@ -1826,13 +1992,9 @@ estimate_numbers_of_iterations_loop (struct loop *loop)
struct tree_niter_desc niter_desc;
/* Give up if we already have tried to compute an estimation. */
if (loop->estimated_nb_iterations == chrec_dont_know
/* Or when we already have an estimation. */
|| (loop->estimated_nb_iterations != NULL_TREE
&& TREE_CODE (loop->estimated_nb_iterations) == INTEGER_CST))
if (loop->estimate_state != EST_NOT_COMPUTED)
return;
else
loop->estimated_nb_iterations = chrec_dont_know;
loop->estimate_state = EST_NOT_AVAILABLE;
exits = get_loop_exit_edges (loop, &n_exits);
for (i = 0; i < n_exits; i++)
@ -1842,19 +2004,19 @@ estimate_numbers_of_iterations_loop (struct loop *loop)
niter = niter_desc.niter;
type = TREE_TYPE (niter);
if (!zero_p (niter_desc.may_be_zero)
&& !nonzero_p (niter_desc.may_be_zero))
if (TREE_CODE (niter_desc.may_be_zero) != INTEGER_CST)
niter = build3 (COND_EXPR, type, niter_desc.may_be_zero,
build_int_cst (type, 0),
niter);
record_estimate (loop, niter,
niter_desc.additional_info,
last_stmt (exits[i]->src));
last_stmt (exits[i]->src),
true, true);
}
free (exits);
if (chrec_contains_undetermined (loop->estimated_nb_iterations))
infer_loop_bounds_from_undefined (loop);
infer_loop_bounds_from_undefined (loop);
compute_estimated_nb_iterations (loop);
}
/* Records estimates on numbers of iterations of LOOPS. */
@ -1899,40 +2061,67 @@ stmt_dominates_stmt_p (tree s1, tree s2)
}
/* Returns true when we can prove that the number of executions of
STMT in the loop is at most NITER, according to the fact
that the statement NITER_BOUND->at_stmt is executed at most
NITER_BOUND->bound times. */
STMT in the loop is at most NITER, according to the bound on
the number of executions of the statement NITER_BOUND->stmt recorded in
NITER_BOUND. If STMT is NULL, we must prove this bound for all
statements in the loop. */
static bool
n_of_executions_at_most (tree stmt,
struct nb_iter_bound *niter_bound,
tree niter)
{
tree cond;
tree bound = niter_bound->bound;
tree bound_type = TREE_TYPE (bound);
double_int bound = niter_bound->bound;
tree nit_type = TREE_TYPE (niter);
enum tree_code cmp;
gcc_assert (TYPE_UNSIGNED (bound_type)
&& TYPE_UNSIGNED (nit_type)
&& is_gimple_min_invariant (bound));
if (TYPE_PRECISION (nit_type) > TYPE_PRECISION (bound_type))
bound = fold_convert (nit_type, bound);
else
niter = fold_convert (bound_type, niter);
gcc_assert (TYPE_UNSIGNED (nit_type));
/* After the statement niter_bound->at_stmt we know that anything is
executed at most BOUND times. */
if (stmt && stmt_dominates_stmt_p (niter_bound->at_stmt, stmt))
cmp = GE_EXPR;
/* Before the statement niter_bound->at_stmt we know that anything
is executed at most BOUND + 1 times. */
else
cmp = GT_EXPR;
/* If the bound does not even fit into NIT_TYPE, it cannot tell us that
the number of iterations is small. */
if (!double_int_fits_to_tree_p (nit_type, bound))
return false;
cond = fold_binary (cmp, boolean_type_node, niter, bound);
return nonzero_p (cond);
/* We know that NITER_BOUND->stmt is executed at most NITER_BOUND->bound + 1
times. This means that:
-- if NITER_BOUND->is_exit is true, then everything before
NITER_BOUND->stmt is executed at most NITER_BOUND->bound + 1
times, and everyting after it at most NITER_BOUND->bound times.
-- If NITER_BOUND->is_exit is false, then if we can prove that when STMT
is executed, then NITER_BOUND->stmt is executed as well in the same
iteration (we conclude that if both statements belong to the same
basic block, or if STMT is after NITER_BOUND->stmt), then STMT
is executed at most NITER_BOUND->bound + 1 times. Otherwise STMT is
executed at most NITER_BOUND->bound + 2 times. */
if (niter_bound->is_exit)
{
if (stmt
&& stmt != niter_bound->stmt
&& stmt_dominates_stmt_p (niter_bound->stmt, stmt))
cmp = GE_EXPR;
else
cmp = GT_EXPR;
}
else
{
if (!stmt
|| (bb_for_stmt (stmt) != bb_for_stmt (niter_bound->stmt)
&& !stmt_dominates_stmt_p (niter_bound->stmt, stmt)))
{
bound = double_int_add (bound, double_int_one);
if (double_int_zero_p (bound)
|| !double_int_fits_to_tree_p (nit_type, bound))
return false;
}
cmp = GT_EXPR;
}
return nonzero_p (fold_binary (cmp, boolean_type_node,
niter,
double_int_to_tree (nit_type, bound)));
}
/* Returns true if the arithmetics in TYPE can be assumed not to wrap. */
@ -2042,7 +2231,7 @@ free_numbers_of_iterations_estimates_loop (struct loop *loop)
struct nb_iter_bound *bound, *next;
loop->nb_iterations = NULL;
loop->estimated_nb_iterations = NULL;
loop->estimate_state = EST_NOT_COMPUTED;
for (bound = loop->bounds; bound; bound = next)
{
next = bound->next;
@ -2075,6 +2264,4 @@ void
substitute_in_loop_info (struct loop *loop, tree name, tree val)
{
loop->nb_iterations = simplify_replace_tree (loop->nb_iterations, name, val);
loop->estimated_nb_iterations
= simplify_replace_tree (loop->estimated_nb_iterations, name, val);
}