From 8132a837da9fb586a5fd2596528178f9ea6f97a2 Mon Sep 17 00:00:00 2001 From: Martin Jambor Date: Thu, 18 Mar 2010 21:07:13 +0100 Subject: [PATCH] re PR middle-end/42450 (another GCC 4.5 ICE on C++ templated code) 2010-03-18 Martin Jambor PR middle-end/42450 * cgraph.h (cgraph_redirect_edge_call_stmt_to_callee): Declare. * cgraphunit.c (cgraph_materialize_all_clones): Update calls in all non-clones. Moved call redirection... (cgraph_redirect_edge_call_stmt_to_callee): ...to this new function. (cgraph_materialize_all_clones): Dispose of all combined_args_to_skip bitmaps. (verify_cgraph_node): Do not check for edges pointing to wrong nodes in inline clones. * tree-inline.c (copy_bb): Call cgraph_redirect_edge_call_stmt_to_callee. * ipa.c (cgraph_remove_unreachable_nodes): Call cgraph_node_remove_callees even when there are used clones. * testsuite/g++.dg/torture/pr42450.C: New test. From-SVN: r157546 --- gcc/ChangeLog | 17 ++++ gcc/cgraph.h | 2 +- gcc/cgraphunit.c | 123 +++++++++++++------------ gcc/ipa.c | 2 +- gcc/testsuite/ChangeLog | 5 + gcc/testsuite/g++.dg/torture/pr42450.C | 112 ++++++++++++++++++++++ gcc/tree-inline.c | 1 + 7 files changed, 203 insertions(+), 59 deletions(-) create mode 100644 gcc/testsuite/g++.dg/torture/pr42450.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 04ef3ca7b1f..9b5211c1f3b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,20 @@ +2010-03-18 Martin Jambor + + PR middle-end/42450 + * cgraph.h (cgraph_redirect_edge_call_stmt_to_callee): Declare. + * cgraphunit.c (cgraph_materialize_all_clones): Update calls in + all non-clones. Moved call redirection... + (cgraph_redirect_edge_call_stmt_to_callee): ...to this new + function. + (cgraph_materialize_all_clones): Dispose of all + combined_args_to_skip bitmaps. + (verify_cgraph_node): Do not check for edges pointing to wrong + nodes in inline clones. + * tree-inline.c (copy_bb): Call + cgraph_redirect_edge_call_stmt_to_callee. + * ipa.c (cgraph_remove_unreachable_nodes): Call + cgraph_node_remove_callees even when there are used clones. + 2010-03-18 H.J. Lu * config/i386/libgcc-glibc.ver: Make GCC_4.5.0 inherit GCC_4.4.0. diff --git a/gcc/cgraph.h b/gcc/cgraph.h index f8d52ebc560..4f0c333a3f4 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -534,7 +534,7 @@ void cgraph_remove_edge_duplication_hook (struct cgraph_2edge_hook_list *); struct cgraph_2node_hook_list *cgraph_add_node_duplication_hook (cgraph_2node_hook, void *); void cgraph_remove_node_duplication_hook (struct cgraph_2node_hook_list *); void cgraph_materialize_all_clones (void); - +gimple cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *); /* In cgraphbuild.c */ unsigned int rebuild_cgraph_edges (void); void reset_inline_failed (struct cgraph_node *); diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index f4580adbd75..c41477bede3 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -751,8 +751,9 @@ verify_cgraph_node (struct cgraph_node *node) debug_tree (e->callee->decl); error_found = true; } - else if (!clone_of_p (cgraph_node (decl), e->callee) - && !e->callee->global.inlined_to) + else if (!node->global.inlined_to + && !e->callee->global.inlined_to + && !clone_of_p (cgraph_node (decl), e->callee)) { error ("edge points to wrong declaration:"); debug_tree (e->callee->decl); @@ -2222,11 +2223,60 @@ cgraph_materialize_clone (struct cgraph_node *node) bitmap_obstack_release (NULL); } +/* If necessary, change the function declaration in the call statement + associated with E so that it corresponds to the edge callee. */ + +gimple +cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e) +{ + tree decl = gimple_call_fndecl (e->call_stmt); + gimple new_stmt; + gimple_stmt_iterator gsi; + + if (!decl || decl == e->callee->decl + /* Don't update call from same body alias to the real function. */ + || cgraph_get_node (decl) == cgraph_get_node (e->callee->decl)) + return e->call_stmt; + + if (cgraph_dump_file) + { + fprintf (cgraph_dump_file, "updating call of %s/%i -> %s/%i: ", + cgraph_node_name (e->caller), e->caller->uid, + cgraph_node_name (e->callee), e->callee->uid); + print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags); + } + + if (e->callee->clone.combined_args_to_skip) + new_stmt = gimple_call_copy_skip_args (e->call_stmt, + e->callee->clone.combined_args_to_skip); + else + new_stmt = e->call_stmt; + if (gimple_vdef (new_stmt) + && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME) + SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; + gimple_call_set_fndecl (new_stmt, e->callee->decl); + + gsi = gsi_for_stmt (e->call_stmt); + gsi_replace (&gsi, new_stmt, true); + + /* Update EH information too, just in case. */ + maybe_clean_or_replace_eh_stmt (e->call_stmt, new_stmt); + + cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt); + + if (cgraph_dump_file) + { + fprintf (cgraph_dump_file, " updated to:"); + print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags); + } + return new_stmt; +} + /* Once all functions from compilation unit are in memory, produce all clones - and update all calls. - We might also do this on demand if we don't want to bring all functions to - memory prior compilation, but current WHOPR implementation does that and it is - is bit easier to keep everything right in this order. */ + and update all calls. We might also do this on demand if we don't want to + bring all functions to memory prior compilation, but current WHOPR + implementation does that and it is is bit easier to keep everything right in + this order. */ void cgraph_materialize_all_clones (void) { @@ -2302,69 +2352,28 @@ cgraph_materialize_all_clones (void) if (cgraph_dump_file) fprintf (cgraph_dump_file, "Updating call sites\n"); for (node = cgraph_nodes; node; node = node->next) - if (node->analyzed && gimple_has_body_p (node->decl) - && (!node->clone_of || node->clone_of->decl != node->decl)) + if (node->analyzed && !node->clone_of + && gimple_has_body_p (node->decl)) { struct cgraph_edge *e; current_function_decl = node->decl; push_cfun (DECL_STRUCT_FUNCTION (node->decl)); for (e = node->callees; e; e = e->next_callee) - { - tree decl = gimple_call_fndecl (e->call_stmt); - /* When function gets inlined, indirect inlining might've invented - new edge for orginally indirect stmt. Since we are not - preserving clones in the original form, we must not update here - since other inline clones don't need to contain call to the same - call. Inliner will do the substitution for us later. */ - if (decl && decl != e->callee->decl) - { - gimple new_stmt; - gimple_stmt_iterator gsi; - - if (cgraph_get_node (decl) == cgraph_get_node (e->callee->decl)) - /* Don't update call from same body alias to the real function. */ - continue; - - if (cgraph_dump_file) - { - fprintf (cgraph_dump_file, "updating call of %s in %s:", - cgraph_node_name (node), - cgraph_node_name (e->callee)); - print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags); - } - - if (e->callee->clone.combined_args_to_skip) - new_stmt = gimple_call_copy_skip_args (e->call_stmt, - e->callee->clone.combined_args_to_skip); - else - new_stmt = e->call_stmt; - if (gimple_vdef (new_stmt) - && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME) - SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; - gimple_call_set_fndecl (new_stmt, e->callee->decl); - - gsi = gsi_for_stmt (e->call_stmt); - gsi_replace (&gsi, new_stmt, true); - - /* Update EH information too, just in case. */ - maybe_clean_or_replace_eh_stmt (e->call_stmt, new_stmt); - - cgraph_set_call_stmt_including_clones (node, e->call_stmt, new_stmt); - - if (cgraph_dump_file) - { - fprintf (cgraph_dump_file, " updated to:"); - print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags); - } - } - } + cgraph_redirect_edge_call_stmt_to_callee (e); pop_cfun (); current_function_decl = NULL; #ifdef ENABLE_CHECKING verify_cgraph_node (node); #endif } + if (cgraph_dump_file) + fprintf (cgraph_dump_file, "Materialization Call site updates done.\n"); + /* All changes to parameters have been performed. In order not to + incorrectly repeat them, we simply dispose of the bitmaps that drive the + changes. */ + for (node = cgraph_nodes; node; node = node->next) + node->clone.combined_args_to_skip = NULL; #ifdef ENABLE_CHECKING verify_cgraph (); #endif diff --git a/gcc/ipa.c b/gcc/ipa.c index f81d41a4a28..c789a29372d 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -262,10 +262,10 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) if (!clone) { cgraph_release_function_body (node); - cgraph_node_remove_callees (node); node->analyzed = false; node->local.inlinable = false; } + cgraph_node_remove_callees (node); if (node->prev_sibling_clone) node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone; else if (node->clone_of) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4a0ef166218..8d678f9d8ad 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2010-03-18 Martin Jambor + + PR middle-end/42450 + * g++.dg/torture/pr42450.C: New test. + 2010-03-18 Michael Matz PR middle-end/43419 diff --git a/gcc/testsuite/g++.dg/torture/pr42450.C b/gcc/testsuite/g++.dg/torture/pr42450.C new file mode 100644 index 00000000000..f630fa2b7e2 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/pr42450.C @@ -0,0 +1,112 @@ +/* { dg-do compile } */ + +template < typename > class basic_stringstream; + +struct basic_string { + basic_string(); +}; + +struct ios_base { + virtual ~ios_base(); +}; + +class ostream:ios_base {}; +class istream:virtual ios_base {}; + +template < typename > struct basic_iostream:public istream, ostream { + ~basic_iostream () {} +}; +extern template class basic_iostream < char >; + +template < typename > struct basic_stringstream:public basic_iostream < char > { + basic_string _M_stringbuf; + ~basic_stringstream () {} +}; +extern template class basic_stringstream < char >; + +template < typename > struct AnyMatrixBase; +template < typename, int _Rows, int _Cols, int = _Rows, int = _Cols > class Matrix; +template < typename > class CwiseNullaryOp; + +template < typename Derived > struct MatrixBase:public AnyMatrixBase < Derived > { + typedef CwiseNullaryOp < Derived > ConstantReturnType; + ConstantReturnType Constant (); + template < typename > Derived cast (); + static CwiseNullaryOp < Derived > Random (int); +}; + +template < typename Derived > struct AnyMatrixBase { + Derived derived () {} + Derived & derived () const {} +}; + +template < typename, int > struct ei_matrix_storage {}; + +template < typename _Scalar, int, int, int _MaxRows, int _MaxCols > struct Matrix:MatrixBase < Matrix < _Scalar, _MaxRows, _MaxCols > > { + typedef MatrixBase < Matrix > Base; + ei_matrix_storage < int, _MaxCols > m_storage; + Matrix operator= (const Matrix other) { + _resize_to_match (other); + lazyAssign (other.derived ()); + } + template < typename OtherDerived > Matrix lazyAssign (MatrixBase < OtherDerived > other) { + _resize_to_match (other); + return Base (other.derived ()); + } + Matrix (); + template < typename OtherDerived > Matrix (const MatrixBase < OtherDerived > &other) { + *this = other; + } + template < typename OtherDerived > void _resize_to_match (const MatrixBase < OtherDerived > &) { + throw 1; + } +}; + +template < typename MatrixType > class CwiseNullaryOp: +public MatrixBase < CwiseNullaryOp < MatrixType > > {}; + +int f() +{ + bool align_cols; + if (align_cols) { + basic_stringstream sstr; + f(); + } +} + +template < typename > struct AutoDiffScalar; +template < typename Functor > struct AutoDiffJacobian:Functor { + AutoDiffJacobian (Functor); + typedef typename Functor::InputType InputType; + typedef typename Functor::ValueType ValueType; + typedef Matrix < int, Functor::InputsAtCompileTime, 1 > DerivativeType; + typedef AutoDiffScalar < DerivativeType > ActiveScalar; + typedef Matrix < ActiveScalar, Functor::InputsAtCompileTime, 1 > ActiveInput; + void operator () (InputType x, ValueType *) { + ActiveInput ax = x.template cast < ActiveScalar > (); + } +}; + +template < int NX, int NY > struct TestFunc1 { + enum { + InputsAtCompileTime = NX + }; + typedef Matrix < float, NX, 1 > InputType; + typedef Matrix < float, NY, 1 > ValueType; + typedef Matrix < float, NY, NX > JacobianType; + int inputs (); +}; + +template < typename Func > void forward_jacobian (Func f) { + typename Func::InputType x = Func::InputType::Random (f.inputs ()); + typename Func::ValueType y; + typename Func::JacobianType jref = jref.Constant (); + AutoDiffJacobian < Func > autoj (f); + autoj (x, &y); +} + +void test_autodiff_scalar () +{ + forward_jacobian (TestFunc1 < 2, 2 > ()); + forward_jacobian (TestFunc1 < 3, 2 > ()); +} diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index f3c420475df..e0928b9fe03 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -1650,6 +1650,7 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, bb->frequency, copy_basic_block->frequency); } + stmt = cgraph_redirect_edge_call_stmt_to_callee (edge); } break;