tree-ssa-loop-niter.c (double_int_cmp, [...]): New functions.
* tree-ssa-loop-niter.c (double_int_cmp, bound_index, discover_iteration_bound_by_body_walk): New functions. (discover_iteration_bound_by_body_walk): Use it. * gcc.dg/tree-ssa/loop-38.c: New testcase. From-SVN: r193104
This commit is contained in:
parent
161b371fc5
commit
73ddf95bf1
4 changed files with 248 additions and 0 deletions
|
@ -1,3 +1,9 @@
|
|||
2012-11-02 Jan Hubicka <jh@suse.cz>
|
||||
|
||||
* tree-ssa-loop-niter.c (double_int_cmp, bound_index,
|
||||
discover_iteration_bound_by_body_walk): New functions.
|
||||
(discover_iteration_bound_by_body_walk): Use it.
|
||||
|
||||
2012-11-02 Jan Hubicka <jh@suse.cz>
|
||||
|
||||
* predict.c (predict_loops): Predict also exits not dominating
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
* gcc.target/powerpc/pr46728-8.c: Likewise.
|
||||
* gcc/testsuite/gcc.dg/pr46728-6.c: Likewise.
|
||||
|
||||
2012-11-02 Jan Hubicka <jh@suse.cz>
|
||||
|
||||
* gcc.dg/tree-ssa/loop-38.c: New testcase.
|
||||
|
||||
2012-11-02 Jan Hubicka <jh@suse.cz>
|
||||
|
||||
* gcc.dg/tree-ssa/cunroll-10.c: New testcase.
|
||||
|
|
18
gcc/testsuite/gcc.dg/tree-ssa/loop-38.c
Normal file
18
gcc/testsuite/gcc.dg/tree-ssa/loop-38.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-cunrolli-details" } */
|
||||
int a[10];
|
||||
int b[11];
|
||||
t(int n)
|
||||
{
|
||||
int i;
|
||||
int sum = 0;
|
||||
for (i=0;i<n;i++)
|
||||
if (q())
|
||||
sum+=a[i];
|
||||
else
|
||||
sum+=b[i];
|
||||
return sum;
|
||||
}
|
||||
/* { dg-final { scan-tree-dump "Found better loop bound 10" "cunrolli" } } */
|
||||
/* { dg-final { scan-tree-dump "Loop 1 iterates at most 10 times" "cunrolli" } } */
|
||||
/* { dg-final { cleanup-tree-dump "cunrolli" } } */
|
|
@ -2961,6 +2961,224 @@ gcov_type_to_double_int (gcov_type val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Compare double ints, callback for qsort. */
|
||||
|
||||
int
|
||||
double_int_cmp (const void *p1, const void *p2)
|
||||
{
|
||||
const double_int *d1 = (const double_int *)p1;
|
||||
const double_int *d2 = (const double_int *)p2;
|
||||
if (*d1 == *d2)
|
||||
return 0;
|
||||
if (d1->ult (*d2))
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return index of BOUND in BOUNDS array sorted in increasing order.
|
||||
Lookup by binary search. */
|
||||
|
||||
int
|
||||
bound_index (VEC (double_int, heap) *bounds, double_int bound)
|
||||
{
|
||||
unsigned int end = VEC_length (double_int, bounds);
|
||||
unsigned int begin = 0;
|
||||
|
||||
/* Find a matching index by means of a binary search. */
|
||||
while (begin != end)
|
||||
{
|
||||
unsigned int middle = (begin + end) / 2;
|
||||
double_int index = VEC_index (double_int, bounds, middle);
|
||||
|
||||
if (index == bound)
|
||||
return middle;
|
||||
else if (index.ult (bound))
|
||||
begin = middle + 1;
|
||||
else
|
||||
end = middle;
|
||||
}
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Used to hold vector of queues of basic blocks bellow. */
|
||||
typedef VEC (basic_block, heap) *bb_queue;
|
||||
DEF_VEC_P(bb_queue);
|
||||
DEF_VEC_ALLOC_P(bb_queue,heap);
|
||||
|
||||
/* We recorded loop bounds only for statements dominating loop latch (and thus
|
||||
executed each loop iteration). If there are any bounds on statements not
|
||||
dominating the loop latch we can improve the estimate by walking the loop
|
||||
body and seeing if every path from loop header to loop latch contains
|
||||
some bounded statement. */
|
||||
|
||||
static void
|
||||
discover_iteration_bound_by_body_walk (struct loop *loop)
|
||||
{
|
||||
pointer_map_t *bb_bounds;
|
||||
struct nb_iter_bound *elt;
|
||||
VEC (double_int, heap) *bounds = NULL;
|
||||
VEC (bb_queue, heap) *queues = NULL;
|
||||
bb_queue queue = NULL;
|
||||
ptrdiff_t queue_index;
|
||||
ptrdiff_t latch_index = 0;
|
||||
pointer_map_t *block_priority;
|
||||
|
||||
/* Discover what bounds may interest us. */
|
||||
for (elt = loop->bounds; elt; elt = elt->next)
|
||||
{
|
||||
double_int bound = elt->bound;
|
||||
|
||||
/* Exit terminates loop at given iteration, while non-exits produce undefined
|
||||
effect on the next iteration. */
|
||||
if (!elt->is_exit)
|
||||
bound += double_int_one;
|
||||
|
||||
if (!loop->any_upper_bound
|
||||
|| bound.ult (loop->nb_iterations_upper_bound))
|
||||
VEC_safe_push (double_int, heap, bounds, bound);
|
||||
}
|
||||
|
||||
/* Exit early if there is nothing to do. */
|
||||
if (!bounds)
|
||||
return;
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
fprintf (dump_file, " Trying to walk loop body to reduce the bound.\n");
|
||||
|
||||
/* Sort the bounds in decreasing order. */
|
||||
qsort (VEC_address (double_int, bounds), VEC_length (double_int, bounds),
|
||||
sizeof (double_int), double_int_cmp);
|
||||
|
||||
/* For every basic block record the lowest bound that is guaranteed to
|
||||
terminate the loop. */
|
||||
|
||||
bb_bounds = pointer_map_create ();
|
||||
for (elt = loop->bounds; elt; elt = elt->next)
|
||||
{
|
||||
double_int bound = elt->bound;
|
||||
if (!elt->is_exit)
|
||||
bound += double_int_one;
|
||||
|
||||
if (!loop->any_upper_bound
|
||||
|| bound.ult (loop->nb_iterations_upper_bound))
|
||||
{
|
||||
ptrdiff_t index = bound_index (bounds, bound);
|
||||
void **entry = pointer_map_contains (bb_bounds,
|
||||
gimple_bb (elt->stmt));
|
||||
if (!entry)
|
||||
*pointer_map_insert (bb_bounds,
|
||||
gimple_bb (elt->stmt)) = (void *)index;
|
||||
else if ((ptrdiff_t)*entry > index)
|
||||
*entry = (void *)index;
|
||||
}
|
||||
}
|
||||
|
||||
block_priority = pointer_map_create ();
|
||||
|
||||
/* Perform shortest path discovery loop->header ... loop->latch.
|
||||
|
||||
The "distance" is given by the smallest loop bound of basic block
|
||||
present in the path and we look for path with largest smallest bound
|
||||
on it.
|
||||
|
||||
To avoid the need for fibonaci heap on double ints we simply compress
|
||||
double ints into indexes to BOUNDS array and then represent the queue
|
||||
as arrays of queues for every index.
|
||||
Index of VEC_length (BOUNDS) means that the execution of given BB has
|
||||
no bounds determined.
|
||||
|
||||
VISITED is a pointer map translating basic block into smallest index
|
||||
it was inserted into the priority queue with. */
|
||||
latch_index = -1;
|
||||
|
||||
/* Start walk in loop header with index set to infinite bound. */
|
||||
queue_index = VEC_length (double_int, bounds);
|
||||
VEC_safe_grow_cleared (bb_queue, heap, queues, queue_index + 1);
|
||||
VEC_safe_push (basic_block, heap, queue, loop->header);
|
||||
VEC_replace (bb_queue, queues, queue_index, queue);
|
||||
*pointer_map_insert (block_priority, loop->header) = (void *)queue_index;
|
||||
|
||||
for (; queue_index >= 0; queue_index--)
|
||||
{
|
||||
if (latch_index < queue_index)
|
||||
{
|
||||
while (VEC_length (basic_block,
|
||||
VEC_index (bb_queue, queues, queue_index)))
|
||||
{
|
||||
basic_block bb;
|
||||
ptrdiff_t bound_index = queue_index;
|
||||
void **entry;
|
||||
edge e;
|
||||
edge_iterator ei;
|
||||
|
||||
queue = VEC_index (bb_queue, queues, queue_index);
|
||||
bb = VEC_pop (basic_block, queue);
|
||||
|
||||
/* OK, we later inserted the BB with lower priority, skip it. */
|
||||
if ((ptrdiff_t)*pointer_map_contains (block_priority, bb) > queue_index)
|
||||
continue;
|
||||
|
||||
/* See if we can improve the bound. */
|
||||
entry = pointer_map_contains (bb_bounds, bb);
|
||||
if (entry && (ptrdiff_t)*entry < bound_index)
|
||||
bound_index = (ptrdiff_t)*entry;
|
||||
|
||||
/* Insert succesors into the queue, watch for latch edge
|
||||
and record greatest index we saw. */
|
||||
FOR_EACH_EDGE (e, ei, bb->succs)
|
||||
{
|
||||
bool insert = false;
|
||||
void **entry;
|
||||
|
||||
if (loop_exit_edge_p (loop, e))
|
||||
continue;
|
||||
|
||||
if (e == loop_latch_edge (loop)
|
||||
&& latch_index < bound_index)
|
||||
latch_index = bound_index;
|
||||
else if (!(entry = pointer_map_contains (block_priority, e->dest)))
|
||||
{
|
||||
insert = true;
|
||||
*pointer_map_insert (block_priority, e->dest) = (void *)bound_index;
|
||||
}
|
||||
else if ((ptrdiff_t)*entry < bound_index)
|
||||
{
|
||||
insert = true;
|
||||
*entry = (void *)bound_index;
|
||||
}
|
||||
|
||||
if (insert)
|
||||
{
|
||||
bb_queue queue2 = VEC_index (bb_queue, queues, bound_index);
|
||||
VEC_safe_push (basic_block, heap, queue2, e->dest);
|
||||
VEC_replace (bb_queue, queues, bound_index, queue2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
VEC_free (basic_block, heap, VEC_index (bb_queue, queues, queue_index));
|
||||
}
|
||||
|
||||
gcc_assert (latch_index >= 0);
|
||||
if (latch_index < VEC_length (double_int, bounds))
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file, "Found better loop bound ");
|
||||
dump_double_int (dump_file,
|
||||
VEC_index (double_int, bounds, latch_index), true);
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
record_niter_bound (loop, VEC_index (double_int, bounds, latch_index),
|
||||
false, true);
|
||||
}
|
||||
|
||||
VEC_free (bb_queue, heap, queues);
|
||||
pointer_map_destroy (bb_bounds);
|
||||
pointer_map_destroy (block_priority);
|
||||
}
|
||||
|
||||
/* See if every path cross the loop goes through a statement that is known
|
||||
to not execute at the last iteration. In that case we can decrese iteration
|
||||
count by 1. */
|
||||
|
@ -3108,6 +3326,8 @@ estimate_numbers_of_iterations_loop (struct loop *loop)
|
|||
|
||||
infer_loop_bounds_from_undefined (loop);
|
||||
|
||||
discover_iteration_bound_by_body_walk (loop);
|
||||
|
||||
maybe_lower_iteration_bound (loop);
|
||||
|
||||
/* If we have a measured profile, use it to estimate the number of
|
||||
|
|
Loading…
Add table
Reference in a new issue