tree-optimization/101769 - tail recursion creates possibly infinite loop
This makes tail recursion optimization produce a loop structure manually rather than relying on loop fixup. That also allows the loop to be marked as finite (it would eventually blow the stack if it were not). 2021-08-04 Richard Biener <rguenther@suse.de> PR tree-optimization/101769 * tree-tailcall.c (eliminate_tail_call): Add the created loop for the first recursion and return it via the new output parameter. (optimize_tail_call): Pass through new output param. (tree_optimize_tail_calls_1): After creating all latches, add the created loop to the loop tree. Do not mark loops for fixup. * g++.dg/tree-ssa/pr101769.C: New testcase.
This commit is contained in:
parent
5c73b94fdc
commit
4d56259101
2 changed files with 77 additions and 13 deletions
56
gcc/testsuite/g++.dg/tree-ssa/pr101769.C
Normal file
56
gcc/testsuite/g++.dg/tree-ssa/pr101769.C
Normal file
|
@ -0,0 +1,56 @@
|
|||
// { dg-do compile }
|
||||
// { dg-require-effective-target c++11 }
|
||||
// { dg-options "-O2 -fdump-tree-optimized" }
|
||||
|
||||
struct Node
|
||||
{
|
||||
Node* right;
|
||||
Node* down;
|
||||
};
|
||||
|
||||
inline
|
||||
void free_node(Node*)
|
||||
{
|
||||
}
|
||||
|
||||
void free_all(Node* n_)
|
||||
{
|
||||
if (n_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
free_all(n_->right);
|
||||
do {
|
||||
Node* t = n_->down;
|
||||
free_node(n_);
|
||||
n_ = t;
|
||||
} while (n_);
|
||||
}
|
||||
|
||||
void free_all2_r(Node* n_)
|
||||
{
|
||||
if (n_->right) {
|
||||
free_all2_r(n_->right);
|
||||
}
|
||||
do {
|
||||
Node* t = n_->down;
|
||||
free_node(n_);
|
||||
n_ = t;
|
||||
} while (n_);
|
||||
}
|
||||
|
||||
void free_all2(Node* n_)
|
||||
{
|
||||
if (n_) {
|
||||
free_all2_r(n_);
|
||||
}
|
||||
}
|
||||
|
||||
void loop(Node* n_)
|
||||
{
|
||||
do {
|
||||
n_ = n_->down;
|
||||
} while (n_);
|
||||
}
|
||||
|
||||
// All functions should be empty.
|
||||
// { dg-final { scan-tree-dump-times "<bb " 4 "optimized" } }
|
|
@ -131,9 +131,6 @@ static tree m_acc, a_acc;
|
|||
|
||||
static bitmap tailr_arg_needs_copy;
|
||||
|
||||
static bool optimize_tail_call (struct tailcall *, bool);
|
||||
static void eliminate_tail_call (struct tailcall *);
|
||||
|
||||
/* Returns false when the function is not suitable for tail call optimization
|
||||
from some reason (e.g. if it takes variable number of arguments). */
|
||||
|
||||
|
@ -926,10 +923,11 @@ decrease_profile (basic_block bb, profile_count count)
|
|||
}
|
||||
|
||||
/* Eliminates tail call described by T. TMP_VARS is a list of
|
||||
temporary variables used to copy the function arguments. */
|
||||
temporary variables used to copy the function arguments.
|
||||
Allocates *NEW_LOOP if not already done and initializes it. */
|
||||
|
||||
static void
|
||||
eliminate_tail_call (struct tailcall *t)
|
||||
eliminate_tail_call (struct tailcall *t, class loop *&new_loop)
|
||||
{
|
||||
tree param, rslt;
|
||||
gimple *stmt, *call;
|
||||
|
@ -999,6 +997,16 @@ eliminate_tail_call (struct tailcall *t)
|
|||
gcc_assert (e);
|
||||
PENDING_STMT (e) = NULL;
|
||||
|
||||
/* Add the new loop. */
|
||||
if (!new_loop)
|
||||
{
|
||||
new_loop = alloc_loop ();
|
||||
new_loop->header = first;
|
||||
new_loop->finite_p = true;
|
||||
}
|
||||
else
|
||||
gcc_assert (new_loop->header == first);
|
||||
|
||||
/* Add phi node entries for arguments. The ordering of the phi nodes should
|
||||
be the same as the ordering of the arguments. */
|
||||
for (param = DECL_ARGUMENTS (current_function_decl),
|
||||
|
@ -1037,11 +1045,12 @@ eliminate_tail_call (struct tailcall *t)
|
|||
mark the tailcalls for the sibcall optimization. */
|
||||
|
||||
static bool
|
||||
optimize_tail_call (struct tailcall *t, bool opt_tailcalls)
|
||||
optimize_tail_call (struct tailcall *t, bool opt_tailcalls,
|
||||
class loop *&new_loop)
|
||||
{
|
||||
if (t->tail_recursion)
|
||||
{
|
||||
eliminate_tail_call (t);
|
||||
eliminate_tail_call (t, new_loop);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1177,12 +1186,15 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
|
|||
opt_tailcalls = false;
|
||||
}
|
||||
|
||||
class loop *new_loop = NULL;
|
||||
for (; tailcalls; tailcalls = next)
|
||||
{
|
||||
next = tailcalls->next;
|
||||
changed |= optimize_tail_call (tailcalls, opt_tailcalls);
|
||||
changed |= optimize_tail_call (tailcalls, opt_tailcalls, new_loop);
|
||||
free (tailcalls);
|
||||
}
|
||||
if (new_loop)
|
||||
add_loop (new_loop, loops_for_fn (cfun)->tree_root);
|
||||
|
||||
if (a_acc || m_acc)
|
||||
{
|
||||
|
@ -1198,11 +1210,7 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
|
|||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
/* We may have created new loops. Make them magically appear. */
|
||||
loops_state_set (LOOPS_NEED_FIXUP);
|
||||
free_dominance_info (CDI_DOMINATORS);
|
||||
}
|
||||
free_dominance_info (CDI_DOMINATORS);
|
||||
|
||||
/* Add phi nodes for the virtual operands defined in the function to the
|
||||
header of the loop created by tail recursion elimination. Do so
|
||||
|
|
Loading…
Add table
Reference in a new issue