We fail to keep the cannot-inline flag up-to-date when turning indirect to direct calls. The following patch arranges to do this during statement folding (which should always be called when that happens). It also makes sure to copy the updated flag to the edge when iterating early inlining.
Bootstrap and regtest running on x86_64-unknown-linux-gnu, ok? Thanks, Richard. 2010-10-28 Richard Guenther <rguent...@suse.de> PR tree-optimization/50890 * gimple.h (gimple_fold_call): Remove. * gimple-fold.c (fold_stmt_1): Move all call related code to ... (gimple_fold_call): ... here. Make static. Update the cannot-inline flag on direct calls. * ipa-inline.c (early_inliner): Copy the cannot-inline flag from the statements to the edges. * gcc.dg/torture/pr50890.c: New testcase. Index: gcc/gimple.h =================================================================== *** gcc/gimple.h (revision 180608) --- gcc/gimple.h (working copy) *************** unsigned get_gimple_rhs_num_ops (enum tr *** 909,915 **** #define gimple_alloc(c, n) gimple_alloc_stat (c, n MEM_STAT_INFO) gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL); const char *gimple_decl_printable_name (tree, int); - bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace); tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree); void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree); tree gimple_extract_devirt_binfo_from_cst (tree); --- 909,914 ---- Index: gcc/gimple-fold.c =================================================================== *** gcc/gimple-fold.c (revision 180608) --- gcc/gimple-fold.c (working copy) *************** gimple_extract_devirt_binfo_from_cst (tr *** 1057,1109 **** simplifies to a constant value. Return true if any changes were made. It is assumed that the operands have been previously folded. */ ! bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace) { gimple stmt = gsi_stmt (*gsi); tree callee; ! /* Check for builtins that CCP can handle using information not ! available in the generic fold routines. */ ! callee = gimple_call_fndecl (stmt); ! if (!inplace && callee && DECL_BUILT_IN (callee)) ! { ! tree result = gimple_fold_builtin (stmt); ! ! if (result) ! { ! if (!update_call_from_tree (gsi, result)) ! gimplify_and_update_call_from_tree (gsi, result); ! return true; ! } ! } /* Check for virtual calls that became direct calls. */ callee = gimple_call_fn (stmt); if (callee && TREE_CODE (callee) == OBJ_TYPE_REF) { - tree binfo, fndecl, obj; - HOST_WIDE_INT token; - if (gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE) { gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee)); ! return true; } ! obj = OBJ_TYPE_REF_OBJECT (callee); ! binfo = gimple_extract_devirt_binfo_from_cst (obj); ! if (!binfo) ! return false; ! token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee)); ! fndecl = gimple_get_virt_method_for_binfo (token, binfo); ! if (!fndecl) ! return false; ! gimple_call_set_fndecl (stmt, fndecl); ! return true; } ! return false; } /* Worker for both fold_stmt and fold_stmt_inplace. The INPLACE argument --- 1057,1138 ---- simplifies to a constant value. Return true if any changes were made. It is assumed that the operands have been previously folded. */ ! static bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace) { gimple stmt = gsi_stmt (*gsi); tree callee; + bool changed = false; + unsigned i; ! /* Fold *& in call arguments. */ ! for (i = 0; i < gimple_call_num_args (stmt); ++i) ! if (REFERENCE_CLASS_P (gimple_call_arg (stmt, i))) ! { ! tree tmp = maybe_fold_reference (gimple_call_arg (stmt, i), false); ! if (tmp) ! { ! gimple_call_set_arg (stmt, i, tmp); ! changed = true; ! } ! } /* Check for virtual calls that became direct calls. */ callee = gimple_call_fn (stmt); if (callee && TREE_CODE (callee) == OBJ_TYPE_REF) { if (gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE) { gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee)); ! changed = true; } + else + { + tree obj = OBJ_TYPE_REF_OBJECT (callee); + tree binfo = gimple_extract_devirt_binfo_from_cst (obj); + if (binfo) + { + HOST_WIDE_INT token + = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee)); + tree fndecl = gimple_get_virt_method_for_binfo (token, binfo); + if (fndecl) + { + gimple_call_set_fndecl (stmt, fndecl); + changed = true; + } + } + } + } ! /* Check whether propagating into the function address made the ! call direct, and thus possibly non-inlineable. ! ??? This asks for a more conservative setting of the non-inlinable ! flag, namely true for all indirect calls. But that would require ! that we can re-compute the flag conservatively, thus it isn't ! ever initialized from something else than return/argument type ! checks . */ ! callee = gimple_call_fndecl (stmt); ! if (callee ! && !gimple_check_call_matching_types (stmt, callee)) ! gimple_call_set_cannot_inline (stmt, true); ! ! if (inplace) ! return changed; ! ! /* Check for builtins that CCP can handle using information not ! available in the generic fold routines. */ ! if (callee && DECL_BUILT_IN (callee)) ! { ! tree result = gimple_fold_builtin (stmt); ! if (result) ! { ! if (!update_call_from_tree (gsi, result)) ! gimplify_and_update_call_from_tree (gsi, result); ! changed = true; ! } } ! return changed; } /* Worker for both fold_stmt and fold_stmt_inplace. The INPLACE argument *************** fold_stmt_1 (gimple_stmt_iterator *gsi, *** 1162,1178 **** break; case GIMPLE_CALL: - /* Fold *& in call arguments. */ - for (i = 0; i < gimple_call_num_args (stmt); ++i) - if (REFERENCE_CLASS_P (gimple_call_arg (stmt, i))) - { - tree tmp = maybe_fold_reference (gimple_call_arg (stmt, i), false); - if (tmp) - { - gimple_call_set_arg (stmt, i, tmp); - changed = true; - } - } changed |= gimple_fold_call (gsi, inplace); break; --- 1191,1196 ---- Index: gcc/ipa-inline.c =================================================================== *** gcc/ipa-inline.c (revision 180608) --- gcc/ipa-inline.c (working copy) *************** early_inliner (void) *** 1949,1954 **** --- 1949,1956 ---- = estimate_num_insns (edge->call_stmt, &eni_size_weights); es->call_stmt_time = estimate_num_insns (edge->call_stmt, &eni_time_weights); + edge->call_stmt_cannot_inline_p + = gimple_call_cannot_inline_p (edge->call_stmt); } timevar_pop (TV_INTEGRATION); iterations++; Index: gcc/testsuite/gcc.dg/torture/pr50890.c =================================================================== *** gcc/testsuite/gcc.dg/torture/pr50890.c (revision 0) --- gcc/testsuite/gcc.dg/torture/pr50890.c (revision 0) *************** *** 0 **** --- 1,17 ---- + /* { dg-do compile } */ + + static float make_insn_raw (void) + { + return 0; + } + + static int emit_pattern_after_noloc (int (make_raw) ()) + { + return make_raw (); + } + + void emit_insn_after_noloc (void) + { + emit_pattern_after_noloc ((void *) make_insn_raw); + } +