re PR lto/65515 (FAIL: gcc.c-torture/compile/limits-fndefn.c -O2 -flto -flto-partition=none (ICE) -- SIGSEGV for stack growth failure)
PR lto/65515 * lto-streamer-out.c (DFS::worklist): New struct. (DFS::worklist_vec): New data member. (DFS::next_dfs_num): Remove. (DFS::DFS): Rewritten using worklist instead of recursion, using most of code from DFS::DFS_write_tree. (DFS::DFS_write_tree_body): Remove SINGLE_P argument, don't pass it to DFS_write_tree calls. (DFS::DFS_write_tree): Remove SINGLE_P argument, after quick initial checks push it into worklist_vec and return. From-SVN: r221656
This commit is contained in:
parent
ea348bbe59
commit
bbf043c2d2
2 changed files with 226 additions and 175 deletions
|
@ -1,3 +1,16 @@
|
|||
2015-03-25 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR lto/65515
|
||||
* lto-streamer-out.c (DFS::worklist): New struct.
|
||||
(DFS::worklist_vec): New data member.
|
||||
(DFS::next_dfs_num): Remove.
|
||||
(DFS::DFS): Rewritten using worklist instead of recursion,
|
||||
using most of code from DFS::DFS_write_tree.
|
||||
(DFS::DFS_write_tree_body): Remove SINGLE_P argument, don't
|
||||
pass it to DFS_write_tree calls.
|
||||
(DFS::DFS_write_tree): Remove SINGLE_P argument, after
|
||||
quick initial checks push it into worklist_vec and return.
|
||||
|
||||
2015-03-25 Richard Biener <rguenther@suse.de>
|
||||
|
||||
PR middle-end/65519
|
||||
|
|
|
@ -485,31 +485,225 @@ private:
|
|||
unsigned int dfsnum;
|
||||
unsigned int low;
|
||||
};
|
||||
struct worklist
|
||||
{
|
||||
tree expr;
|
||||
sccs *from_state;
|
||||
sccs *cstate;
|
||||
bool ref_p;
|
||||
bool this_ref_p;
|
||||
};
|
||||
|
||||
static int scc_entry_compare (const void *, const void *);
|
||||
|
||||
void DFS_write_tree_body (struct output_block *ob,
|
||||
tree expr, sccs *expr_state, bool ref_p,
|
||||
bool single_p);
|
||||
tree expr, sccs *expr_state, bool ref_p);
|
||||
|
||||
void DFS_write_tree (struct output_block *ob, sccs *from_state,
|
||||
tree expr, bool ref_p, bool this_ref_p,
|
||||
bool single_p);
|
||||
tree expr, bool ref_p, bool this_ref_p);
|
||||
|
||||
hashval_t
|
||||
hash_scc (struct output_block *ob, unsigned first, unsigned size);
|
||||
|
||||
unsigned int next_dfs_num;
|
||||
hash_map<tree, sccs *> sccstate;
|
||||
vec<worklist> worklist_vec;
|
||||
struct obstack sccstate_obstack;
|
||||
};
|
||||
|
||||
DFS::DFS (struct output_block *ob, tree expr, bool ref_p, bool this_ref_p,
|
||||
bool single_p)
|
||||
{
|
||||
unsigned int next_dfs_num = 1;
|
||||
sccstack.create (0);
|
||||
gcc_obstack_init (&sccstate_obstack);
|
||||
next_dfs_num = 1;
|
||||
DFS_write_tree (ob, NULL, expr, ref_p, this_ref_p, single_p);
|
||||
worklist_vec = vNULL;
|
||||
DFS_write_tree (ob, NULL, expr, ref_p, this_ref_p);
|
||||
while (!worklist_vec.is_empty ())
|
||||
{
|
||||
worklist &w = worklist_vec.last ();
|
||||
expr = w.expr;
|
||||
sccs *from_state = w.from_state;
|
||||
sccs *cstate = w.cstate;
|
||||
ref_p = w.ref_p;
|
||||
this_ref_p = w.this_ref_p;
|
||||
if (cstate == NULL)
|
||||
{
|
||||
sccs **slot = &sccstate.get_or_insert (expr);
|
||||
cstate = *slot;
|
||||
if (cstate)
|
||||
{
|
||||
gcc_checking_assert (from_state);
|
||||
if (cstate->dfsnum < from_state->dfsnum)
|
||||
from_state->low = MIN (cstate->dfsnum, from_state->low);
|
||||
worklist_vec.pop ();
|
||||
continue;
|
||||
}
|
||||
|
||||
scc_entry e = { expr, 0 };
|
||||
/* Not yet visited. DFS recurse and push it onto the stack. */
|
||||
*slot = cstate = XOBNEW (&sccstate_obstack, struct sccs);
|
||||
sccstack.safe_push (e);
|
||||
cstate->dfsnum = next_dfs_num++;
|
||||
cstate->low = cstate->dfsnum;
|
||||
w.cstate = cstate;
|
||||
|
||||
if (streamer_handle_as_builtin_p (expr))
|
||||
;
|
||||
else if (TREE_CODE (expr) == INTEGER_CST
|
||||
&& !TREE_OVERFLOW (expr))
|
||||
DFS_write_tree (ob, cstate, TREE_TYPE (expr), ref_p, ref_p);
|
||||
else
|
||||
{
|
||||
DFS_write_tree_body (ob, expr, cstate, ref_p);
|
||||
|
||||
/* Walk any LTO-specific edges. */
|
||||
if (DECL_P (expr)
|
||||
&& TREE_CODE (expr) != FUNCTION_DECL
|
||||
&& TREE_CODE (expr) != TRANSLATION_UNIT_DECL)
|
||||
{
|
||||
/* Handle DECL_INITIAL for symbols. */
|
||||
tree initial
|
||||
= get_symbol_initial_value (ob->decl_state->symtab_node_encoder,
|
||||
expr);
|
||||
DFS_write_tree (ob, cstate, initial, ref_p, ref_p);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* See if we found an SCC. */
|
||||
if (cstate->low == cstate->dfsnum)
|
||||
{
|
||||
unsigned first, size;
|
||||
tree x;
|
||||
|
||||
/* If we are re-walking a single leaf-SCC just pop it,
|
||||
let earlier worklist item access the sccstack. */
|
||||
if (single_p)
|
||||
{
|
||||
worklist_vec.pop ();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Pop the SCC and compute its size. */
|
||||
first = sccstack.length ();
|
||||
do
|
||||
{
|
||||
x = sccstack[--first].t;
|
||||
}
|
||||
while (x != expr);
|
||||
size = sccstack.length () - first;
|
||||
|
||||
/* No need to compute hashes for LTRANS units, we don't perform
|
||||
any merging there. */
|
||||
hashval_t scc_hash = 0;
|
||||
unsigned scc_entry_len = 0;
|
||||
if (!flag_wpa)
|
||||
{
|
||||
scc_hash = hash_scc (ob, first, size);
|
||||
|
||||
/* Put the entries with the least number of collisions first. */
|
||||
unsigned entry_start = 0;
|
||||
scc_entry_len = size + 1;
|
||||
for (unsigned i = 0; i < size;)
|
||||
{
|
||||
unsigned from = i;
|
||||
for (i = i + 1; i < size
|
||||
&& (sccstack[first + i].hash
|
||||
== sccstack[first + from].hash); ++i)
|
||||
;
|
||||
if (i - from < scc_entry_len)
|
||||
{
|
||||
scc_entry_len = i - from;
|
||||
entry_start = from;
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < scc_entry_len; ++i)
|
||||
{
|
||||
scc_entry tem = sccstack[first + i];
|
||||
sccstack[first + i] = sccstack[first + entry_start + i];
|
||||
sccstack[first + entry_start + i] = tem;
|
||||
}
|
||||
|
||||
if (scc_entry_len == 1)
|
||||
; /* We already sorted SCC deterministically in hash_scc. */
|
||||
else
|
||||
/* Check that we have only one SCC.
|
||||
Naturally we may have conflicts if hash function is not
|
||||
strong enough. Lets see how far this gets. */
|
||||
{
|
||||
#ifdef ENABLE_CHECKING
|
||||
gcc_unreachable ();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Write LTO_tree_scc. */
|
||||
streamer_write_record_start (ob, LTO_tree_scc);
|
||||
streamer_write_uhwi (ob, size);
|
||||
streamer_write_uhwi (ob, scc_hash);
|
||||
|
||||
/* Write size-1 SCCs without wrapping them inside SCC bundles.
|
||||
All INTEGER_CSTs need to be handled this way as we need
|
||||
their type to materialize them. Also builtins are handled
|
||||
this way.
|
||||
??? We still wrap these in LTO_tree_scc so at the
|
||||
input side we can properly identify the tree we want
|
||||
to ultimatively return. */
|
||||
if (size == 1)
|
||||
lto_output_tree_1 (ob, expr, scc_hash, ref_p, this_ref_p);
|
||||
else
|
||||
{
|
||||
/* Write the size of the SCC entry candidates. */
|
||||
streamer_write_uhwi (ob, scc_entry_len);
|
||||
|
||||
/* Write all headers and populate the streamer cache. */
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
{
|
||||
hashval_t hash = sccstack[first+i].hash;
|
||||
tree t = sccstack[first+i].t;
|
||||
bool exists_p = streamer_tree_cache_insert (ob->writer_cache,
|
||||
t, hash, NULL);
|
||||
gcc_assert (!exists_p);
|
||||
|
||||
if (!lto_is_streamable (t))
|
||||
internal_error ("tree code %qs is not supported "
|
||||
"in LTO streams",
|
||||
get_tree_code_name (TREE_CODE (t)));
|
||||
|
||||
gcc_checking_assert (!streamer_handle_as_builtin_p (t));
|
||||
|
||||
/* Write the header, containing everything needed to
|
||||
materialize EXPR on the reading side. */
|
||||
streamer_write_tree_header (ob, t);
|
||||
}
|
||||
|
||||
/* Write the bitpacks and tree references. */
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
{
|
||||
lto_write_tree_1 (ob, sccstack[first+i].t, ref_p);
|
||||
|
||||
/* Mark the end of the tree. */
|
||||
streamer_write_zero (ob);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally truncate the vector. */
|
||||
sccstack.truncate (first);
|
||||
|
||||
if (from_state)
|
||||
from_state->low = MIN (from_state->low, cstate->low);
|
||||
worklist_vec.pop ();
|
||||
continue;
|
||||
}
|
||||
|
||||
gcc_checking_assert (from_state);
|
||||
from_state->low = MIN (from_state->low, cstate->low);
|
||||
if (cstate->dfsnum < from_state->dfsnum)
|
||||
from_state->low = MIN (cstate->dfsnum, from_state->low);
|
||||
worklist_vec.pop ();
|
||||
}
|
||||
worklist_vec.release ();
|
||||
}
|
||||
|
||||
DFS::~DFS ()
|
||||
|
@ -523,11 +717,10 @@ DFS::~DFS ()
|
|||
|
||||
void
|
||||
DFS::DFS_write_tree_body (struct output_block *ob,
|
||||
tree expr, sccs *expr_state, bool ref_p,
|
||||
bool single_p)
|
||||
tree expr, sccs *expr_state, bool ref_p)
|
||||
{
|
||||
#define DFS_follow_tree_edge(DEST) \
|
||||
DFS_write_tree (ob, expr_state, DEST, ref_p, ref_p, single_p)
|
||||
DFS_write_tree (ob, expr_state, DEST, ref_p, ref_p)
|
||||
|
||||
enum tree_code code;
|
||||
|
||||
|
@ -680,7 +873,7 @@ DFS::DFS_write_tree_body (struct output_block *ob,
|
|||
/* We have to stream externals in the block chain as
|
||||
non-references. See also
|
||||
tree-streamer-out.c:streamer_write_chain. */
|
||||
DFS_write_tree (ob, expr_state, t, ref_p, false, single_p);
|
||||
DFS_write_tree (ob, expr_state, t, ref_p, false);
|
||||
else
|
||||
DFS_follow_tree_edge (t);
|
||||
|
||||
|
@ -1339,10 +1532,8 @@ DFS::hash_scc (struct output_block *ob,
|
|||
|
||||
void
|
||||
DFS::DFS_write_tree (struct output_block *ob, sccs *from_state,
|
||||
tree expr, bool ref_p, bool this_ref_p, bool single_p)
|
||||
tree expr, bool ref_p, bool this_ref_p)
|
||||
{
|
||||
unsigned ix;
|
||||
|
||||
/* Handle special cases. */
|
||||
if (expr == NULL_TREE)
|
||||
return;
|
||||
|
@ -1352,169 +1543,16 @@ DFS::DFS_write_tree (struct output_block *ob, sccs *from_state,
|
|||
return;
|
||||
|
||||
/* Check if we already streamed EXPR. */
|
||||
if (streamer_tree_cache_lookup (ob->writer_cache, expr, &ix))
|
||||
if (streamer_tree_cache_lookup (ob->writer_cache, expr, NULL))
|
||||
return;
|
||||
|
||||
sccs **slot = &sccstate.get_or_insert (expr);
|
||||
sccs *cstate = *slot;
|
||||
if (!cstate)
|
||||
{
|
||||
scc_entry e = { expr, 0 };
|
||||
/* Not yet visited. DFS recurse and push it onto the stack. */
|
||||
*slot = cstate = XOBNEW (&sccstate_obstack, struct sccs);
|
||||
sccstack.safe_push (e);
|
||||
cstate->dfsnum = next_dfs_num++;
|
||||
cstate->low = cstate->dfsnum;
|
||||
|
||||
if (streamer_handle_as_builtin_p (expr))
|
||||
;
|
||||
else if (TREE_CODE (expr) == INTEGER_CST
|
||||
&& !TREE_OVERFLOW (expr))
|
||||
DFS_write_tree (ob, cstate, TREE_TYPE (expr), ref_p, ref_p, single_p);
|
||||
else
|
||||
{
|
||||
DFS_write_tree_body (ob, expr, cstate, ref_p, single_p);
|
||||
|
||||
/* Walk any LTO-specific edges. */
|
||||
if (DECL_P (expr)
|
||||
&& TREE_CODE (expr) != FUNCTION_DECL
|
||||
&& TREE_CODE (expr) != TRANSLATION_UNIT_DECL)
|
||||
{
|
||||
/* Handle DECL_INITIAL for symbols. */
|
||||
tree initial = get_symbol_initial_value (ob->decl_state->symtab_node_encoder,
|
||||
expr);
|
||||
DFS_write_tree (ob, cstate, initial, ref_p, ref_p, single_p);
|
||||
}
|
||||
}
|
||||
|
||||
/* See if we found an SCC. */
|
||||
if (cstate->low == cstate->dfsnum)
|
||||
{
|
||||
unsigned first, size;
|
||||
tree x;
|
||||
|
||||
/* If we are re-walking a single leaf-SCC just return and
|
||||
let the caller access the sccstack. */
|
||||
if (single_p)
|
||||
return;
|
||||
|
||||
/* Pop the SCC and compute its size. */
|
||||
first = sccstack.length ();
|
||||
do
|
||||
{
|
||||
x = sccstack[--first].t;
|
||||
}
|
||||
while (x != expr);
|
||||
size = sccstack.length () - first;
|
||||
|
||||
/* No need to compute hashes for LTRANS units, we don't perform
|
||||
any merging there. */
|
||||
hashval_t scc_hash = 0;
|
||||
unsigned scc_entry_len = 0;
|
||||
if (!flag_wpa)
|
||||
{
|
||||
scc_hash = hash_scc (ob, first, size);
|
||||
|
||||
/* Put the entries with the least number of collisions first. */
|
||||
unsigned entry_start = 0;
|
||||
scc_entry_len = size + 1;
|
||||
for (unsigned i = 0; i < size;)
|
||||
{
|
||||
unsigned from = i;
|
||||
for (i = i + 1; i < size
|
||||
&& (sccstack[first + i].hash
|
||||
== sccstack[first + from].hash); ++i)
|
||||
;
|
||||
if (i - from < scc_entry_len)
|
||||
{
|
||||
scc_entry_len = i - from;
|
||||
entry_start = from;
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < scc_entry_len; ++i)
|
||||
{
|
||||
scc_entry tem = sccstack[first + i];
|
||||
sccstack[first + i] = sccstack[first + entry_start + i];
|
||||
sccstack[first + entry_start + i] = tem;
|
||||
}
|
||||
|
||||
if (scc_entry_len == 1)
|
||||
; /* We already sorted SCC deterministically in hash_scc. */
|
||||
else
|
||||
/* Check that we have only one SCC.
|
||||
Naturally we may have conflicts if hash function is not
|
||||
strong enough. Lets see how far this gets. */
|
||||
{
|
||||
#ifdef ENABLE_CHECKING
|
||||
gcc_unreachable ();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Write LTO_tree_scc. */
|
||||
streamer_write_record_start (ob, LTO_tree_scc);
|
||||
streamer_write_uhwi (ob, size);
|
||||
streamer_write_uhwi (ob, scc_hash);
|
||||
|
||||
/* Write size-1 SCCs without wrapping them inside SCC bundles.
|
||||
All INTEGER_CSTs need to be handled this way as we need
|
||||
their type to materialize them. Also builtins are handled
|
||||
this way.
|
||||
??? We still wrap these in LTO_tree_scc so at the
|
||||
input side we can properly identify the tree we want
|
||||
to ultimatively return. */
|
||||
if (size == 1)
|
||||
lto_output_tree_1 (ob, expr, scc_hash, ref_p, this_ref_p);
|
||||
else
|
||||
{
|
||||
/* Write the size of the SCC entry candidates. */
|
||||
streamer_write_uhwi (ob, scc_entry_len);
|
||||
|
||||
/* Write all headers and populate the streamer cache. */
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
{
|
||||
hashval_t hash = sccstack[first+i].hash;
|
||||
tree t = sccstack[first+i].t;
|
||||
bool exists_p = streamer_tree_cache_insert (ob->writer_cache,
|
||||
t, hash, &ix);
|
||||
gcc_assert (!exists_p);
|
||||
|
||||
if (!lto_is_streamable (t))
|
||||
internal_error ("tree code %qs is not supported "
|
||||
"in LTO streams",
|
||||
get_tree_code_name (TREE_CODE (t)));
|
||||
|
||||
gcc_checking_assert (!streamer_handle_as_builtin_p (t));
|
||||
|
||||
/* Write the header, containing everything needed to
|
||||
materialize EXPR on the reading side. */
|
||||
streamer_write_tree_header (ob, t);
|
||||
}
|
||||
|
||||
/* Write the bitpacks and tree references. */
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
{
|
||||
lto_write_tree_1 (ob, sccstack[first+i].t, ref_p);
|
||||
|
||||
/* Mark the end of the tree. */
|
||||
streamer_write_zero (ob);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally truncate the vector. */
|
||||
sccstack.truncate (first);
|
||||
|
||||
if (from_state)
|
||||
from_state->low = MIN (from_state->low, cstate->low);
|
||||
return;
|
||||
}
|
||||
|
||||
if (from_state)
|
||||
from_state->low = MIN (from_state->low, cstate->low);
|
||||
}
|
||||
gcc_checking_assert (from_state);
|
||||
if (cstate->dfsnum < from_state->dfsnum)
|
||||
from_state->low = MIN (cstate->dfsnum, from_state->low);
|
||||
worklist w;
|
||||
w.expr = expr;
|
||||
w.from_state = from_state;
|
||||
w.cstate = NULL;
|
||||
w.ref_p = ref_p;
|
||||
w.this_ref_p = this_ref_p;
|
||||
worklist_vec.safe_push (w);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue