/* Function splitting pass
- Copyright (C) 2010, 2011
+ Copyright (C) 2010, 2011, 2012
Free Software Foundation, Inc.
Contributed by Jan Hubicka <jh@suse.cz>
#include "fibheap.h"
#include "params.h"
#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
/* Per basic block info. */
struct split_point best_split_point;
+/* Set of basic blocks that are not allowed to dominate a split point. */
+
+static bitmap forbidden_dominators;
+
static tree find_retval (basic_block return_bb);
/* Callback for walk_stmt_load_store_addr_ops. If T is non-SSA automatic
dump_split_point (FILE * file, struct split_point *current)
{
fprintf (file,
- "Split point at BB %i header time:%i header size: %i"
- " split time: %i split size: %i\n bbs: ",
+ "Split point at BB %i\n"
+ " header time: %i header size: %i\n"
+ " split time: %i split size: %i\n bbs: ",
current->entry_bb->index, current->header_time,
current->header_size, current->split_time, current->split_size);
dump_bitmap (file, current->split_bbs);
return ok;
}
+/* If STMT is a call, check the callee against a list of forbidden
+ predicate functions. If a match is found, look for uses of the
+ call result in condition statements that compare against zero.
+ For each such use, find the block targeted by the condition
+ statement for the nonzero result, and set the bit for this block
+ in the forbidden dominators bitmap. The purpose of this is to avoid
+ selecting a split point where we are likely to lose the chance
+ to optimize away an unused function call. */
+
+static void
+check_forbidden_calls (gimple stmt)
+{
+ imm_use_iterator use_iter;
+ use_operand_p use_p;
+ tree lhs;
+
+ /* At the moment, __builtin_constant_p is the only forbidden
+ predicate function call (see PR49642). */
+ if (!gimple_call_builtin_p (stmt, BUILT_IN_CONSTANT_P))
+ return;
+
+ lhs = gimple_call_lhs (stmt);
+
+ if (!lhs || TREE_CODE (lhs) != SSA_NAME)
+ return;
+
+ FOR_EACH_IMM_USE_FAST (use_p, use_iter, lhs)
+ {
+ tree op1;
+ basic_block use_bb, forbidden_bb;
+ enum tree_code code;
+ edge true_edge, false_edge;
+ gimple use_stmt = USE_STMT (use_p);
+
+ if (gimple_code (use_stmt) != GIMPLE_COND)
+ continue;
+
+ /* Assuming canonical form for GIMPLE_COND here, with constant
+ in second position. */
+ op1 = gimple_cond_rhs (use_stmt);
+ code = gimple_cond_code (use_stmt);
+ use_bb = gimple_bb (use_stmt);
+
+ extract_true_false_edges_from_block (use_bb, &true_edge, &false_edge);
+
+ /* We're only interested in comparisons that distinguish
+ unambiguously from zero. */
+ if (!integer_zerop (op1) || code == LE_EXPR || code == GE_EXPR)
+ continue;
+
+ if (code == EQ_EXPR)
+ forbidden_bb = false_edge->dest;
+ else
+ forbidden_bb = true_edge->dest;
+
+ bitmap_set_bit (forbidden_dominators, forbidden_bb->index);
+ }
+}
+
+/* If BB is dominated by any block in the forbidden dominators set,
+ return TRUE; else FALSE. */
+
+static bool
+dominated_by_forbidden (basic_block bb)
+{
+ unsigned dom_bb;
+ bitmap_iterator bi;
+
+ EXECUTE_IF_SET_IN_BITMAP (forbidden_dominators, 1, dom_bb, bi)
+ {
+ if (dominated_by_p (CDI_DOMINATORS, bb, BASIC_BLOCK (dom_bb)))
+ return true;
+ }
+
+ return false;
+}
+
/* We found an split_point CURRENT. NON_SSA_VARS is bitmap of all non ssa
variables used and RETURN_BB is return basic block.
See if we can split function here. */
" Refused: split part has non-ssa uses\n");
return;
}
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " Accepted!\n");
+
+ /* If the split point is dominated by a forbidden block, reject
+ the split. */
+ if (!bitmap_empty_p (forbidden_dominators)
+ && dominated_by_forbidden (current->entry_bb))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ " Refused: split point dominated by forbidden block\n");
+ return;
+ }
/* See if retval used by return bb is computed by header or split part.
When it is computed by split part, we need to produce return statement
else
current->split_part_set_retval = true;
+ /* split_function fixes up at most one PHI non-virtual PHI node in return_bb,
+ for the return value. If there are other PHIs, give up. */
+ if (return_bb != EXIT_BLOCK_PTR)
+ {
+ gimple_stmt_iterator psi;
+
+ for (psi = gsi_start_phis (return_bb); !gsi_end_p (psi); gsi_next (&psi))
+ if (is_gimple_reg (gimple_phi_result (gsi_stmt (psi)))
+ && !(retval
+ && current->split_part_set_retval
+ && TREE_CODE (retval) == SSA_NAME
+ && !DECL_BY_REFERENCE (DECL_RESULT (current_function_decl))
+ && SSA_NAME_DEF_STMT (retval) == gsi_stmt (psi)))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ " Refused: return bb has extra PHIs\n");
+ return;
+ }
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Accepted!\n");
+
/* At the moment chose split point with lowest frequency and that leaves
out smallest size of header.
In future we might re-consider this heuristics. */
find_return_bb (void)
{
edge e;
- edge_iterator ei;
basic_block return_bb = EXIT_BLOCK_PTR;
+ gimple_stmt_iterator bsi;
+ bool found_return = false;
+ tree retval = NULL_TREE;
- if (EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 1)
- FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
- {
- gimple_stmt_iterator bsi;
- bool found_return = false;
- tree retval = NULL_TREE;
+ if (!single_pred_p (EXIT_BLOCK_PTR))
+ return return_bb;
+
+ e = single_pred_edge (EXIT_BLOCK_PTR);
+ for (bsi = gsi_last_bb (e->src); !gsi_end_p (bsi); gsi_prev (&bsi))
+ {
+ gimple stmt = gsi_stmt (bsi);
+ if (gimple_code (stmt) == GIMPLE_LABEL
+ || is_gimple_debug (stmt)
+ || gimple_clobber_p (stmt))
+ ;
+ else if (gimple_code (stmt) == GIMPLE_ASSIGN
+ && found_return
+ && gimple_assign_single_p (stmt)
+ && (auto_var_in_fn_p (gimple_assign_rhs1 (stmt),
+ current_function_decl)
+ || is_gimple_min_invariant (gimple_assign_rhs1 (stmt)))
+ && retval == gimple_assign_lhs (stmt))
+ ;
+ else if (gimple_code (stmt) == GIMPLE_RETURN)
+ {
+ found_return = true;
+ retval = gimple_return_retval (stmt);
+ }
+ else
+ break;
+ }
+ if (gsi_end_p (bsi) && found_return)
+ return_bb = e->src;
- for (bsi = gsi_last_bb (e->src); !gsi_end_p (bsi); gsi_prev (&bsi))
- {
- gimple stmt = gsi_stmt (bsi);
- if (gimple_code (stmt) == GIMPLE_LABEL
- || is_gimple_debug (stmt))
- ;
- else if (gimple_code (stmt) == GIMPLE_ASSIGN
- && found_return
- && gimple_assign_single_p (stmt)
- && (auto_var_in_fn_p (gimple_assign_rhs1 (stmt),
- current_function_decl)
- || is_gimple_min_invariant
- (gimple_assign_rhs1 (stmt)))
- && retval == gimple_assign_lhs (stmt))
- ;
- else if (gimple_code (stmt) == GIMPLE_RETURN)
- {
- found_return = true;
- retval = gimple_return_retval (stmt);
- }
- else
- break;
- }
- if (gsi_end_p (bsi) && found_return)
- {
- if (retval)
- return e->src;
- else
- return_bb = e->src;
- }
- }
return return_bb;
}
for (bsi = gsi_start_bb (return_bb); !gsi_end_p (bsi); gsi_next (&bsi))
if (gimple_code (gsi_stmt (bsi)) == GIMPLE_RETURN)
return gimple_return_retval (gsi_stmt (bsi));
- else if (gimple_code (gsi_stmt (bsi)) == GIMPLE_ASSIGN)
+ else if (gimple_code (gsi_stmt (bsi)) == GIMPLE_ASSIGN
+ && !gimple_clobber_p (gsi_stmt (bsi)))
return gimple_assign_rhs1 (gsi_stmt (bsi));
return NULL;
}
if (is_gimple_debug (stmt))
continue;
+ if (gimple_clobber_p (stmt))
+ continue;
+
/* FIXME: We can split regions containing EH. We can not however
split RESX, EH_DISPATCH and EH_POINTER referring to same region
into different partitions. This would require tracking of
EH regions and checking in consider_split_point if they
are not used elsewhere. */
- if (gimple_code (stmt) == GIMPLE_RESX
- && stmt_can_throw_external (stmt))
+ if (gimple_code (stmt) == GIMPLE_RESX)
{
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Cannot split: external resx.\n");
+ fprintf (dump_file, "Cannot split: resx.\n");
can_split = false;
}
if (gimple_code (stmt) == GIMPLE_EH_DISPATCH)
way to store builtin_stack_save result in non-SSA variable
since all calls to those are compiler generated. */
case BUILT_IN_APPLY:
+ case BUILT_IN_APPLY_ARGS:
case BUILT_IN_VA_START:
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,
split_function (struct split_point *split_point)
{
VEC (tree, heap) *args_to_pass = NULL;
- bitmap args_to_skip = BITMAP_ALLOC (NULL);
+ bitmap args_to_skip;
tree parm;
int num = 0;
- struct cgraph_node *node;
+ struct cgraph_node *node, *cur_node = cgraph_get_node (current_function_decl);
basic_block return_bb = find_return_bb ();
basic_block call_bb;
gimple_stmt_iterator gsi;
tree retval = NULL, real_retval = NULL;
bool split_part_return_p = false;
gimple last_stmt = NULL;
- bool conv_needed = false;
unsigned int i;
tree arg;
dump_split_point (dump_file, split_point);
}
+ if (cur_node->local.can_change_signature)
+ args_to_skip = BITMAP_ALLOC (NULL);
+ else
+ args_to_skip = NULL;
+
/* Collect the parameters of new function and args_to_skip bitmap. */
for (parm = DECL_ARGUMENTS (current_function_decl);
parm; parm = DECL_CHAIN (parm), num++)
- if (!is_gimple_reg (parm)
- || !gimple_default_def (cfun, parm)
- || !bitmap_bit_p (split_point->ssa_names_to_pass,
- SSA_NAME_VERSION (gimple_default_def (cfun, parm))))
+ if (args_to_skip
+ && (!is_gimple_reg (parm)
+ || !gimple_default_def (cfun, parm)
+ || !bitmap_bit_p (split_point->ssa_names_to_pass,
+ SSA_NAME_VERSION (gimple_default_def (cfun,
+ parm)))))
bitmap_set_bit (args_to_skip, num);
else
{
- arg = gimple_default_def (cfun, parm);
- if (TYPE_MAIN_VARIANT (DECL_ARG_TYPE (parm))
- != TYPE_MAIN_VARIANT (TREE_TYPE (arg)))
+ /* This parm might not have been used up to now, but is going to be
+ used, hence register it. */
+ add_referenced_var (parm);
+ if (is_gimple_reg (parm))
{
- conv_needed = true;
- arg = fold_convert (DECL_ARG_TYPE (parm), arg);
+ arg = gimple_default_def (cfun, parm);
+ if (!arg)
+ {
+ arg = make_ssa_name (parm, gimple_build_nop ());
+ set_default_def (parm, arg);
+ }
}
+ else
+ arg = parm;
+
+ if (!useless_type_conversion_p (DECL_ARG_TYPE (parm), TREE_TYPE (arg)))
+ arg = fold_convert (DECL_ARG_TYPE (parm), arg);
VEC_safe_push (tree, heap, args_to_pass, arg);
}
/* If RETURN_BB has virtual operand PHIs, they must be removed and the
virtual operand marked for renaming as we change the CFG in a way that
- tree-inline is not able to compensate for.
+ tree-inline is not able to compensate for.
Note this can happen whether or not we have a return value. If we have
a return value, then RETURN_BB may have PHIs for real operands too. */
if (return_bb != EXIT_BLOCK_PTR)
{
+ bool phi_p = false;
for (gsi = gsi_start_phis (return_bb); !gsi_end_p (gsi);)
{
gimple stmt = gsi_stmt (gsi);
}
mark_virtual_phi_result_for_renaming (stmt);
remove_phi_node (&gsi, true);
+ phi_p = true;
}
+ /* In reality we have to rename the reaching definition of the
+ virtual operand at return_bb as we will eventually release it
+ when we remove the code region we outlined.
+ So we have to rename all immediate virtual uses of that region
+ if we didn't see a PHI definition yet. */
+ /* ??? In real reality we want to set the reaching vdef of the
+ entry of the SESE region as the vuse of the call and the reaching
+ vdef of the exit of the SESE region as the vdef of the call. */
+ if (!phi_p)
+ for (gsi = gsi_start_bb (return_bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ if (gimple_vuse (stmt))
+ {
+ gimple_set_vuse (stmt, NULL_TREE);
+ update_stmt (stmt);
+ }
+ if (gimple_vdef (stmt))
+ break;
+ }
}
/* Now create the actual clone. */
rebuild_cgraph_edges ();
- node = cgraph_function_versioning (cgraph_node (current_function_decl),
- NULL, NULL,
- args_to_skip,
+ node = cgraph_function_versioning (cur_node, NULL, NULL, args_to_skip,
+ !split_part_return_p,
split_point->split_bbs,
split_point->entry_bb, "part");
/* For usual cloning it is enough to clear builtin only when signature
DECL_BUILT_IN_CLASS (node->decl) = NOT_BUILT_IN;
DECL_FUNCTION_CODE (node->decl) = (enum built_in_function) 0;
}
- cgraph_node_remove_callees (cgraph_node (current_function_decl));
+ cgraph_node_remove_callees (cur_node);
if (!split_part_return_p)
TREE_THIS_VOLATILE (node->decl) = 1;
if (dump_file)
/* Produce the call statement. */
gsi = gsi_last_bb (call_bb);
- if (conv_needed)
- FOR_EACH_VEC_ELT (tree, args_to_pass, i, arg)
- if (!is_gimple_val (arg))
- {
- arg = force_gimple_operand_gsi (&gsi, arg, true, NULL_TREE,
- false, GSI_NEW_STMT);
- VEC_replace (tree, args_to_pass, i, arg);
- }
+ FOR_EACH_VEC_ELT (tree, args_to_pass, i, arg)
+ if (!is_gimple_val (arg))
+ {
+ arg = force_gimple_operand_gsi (&gsi, arg, true, NULL_TREE,
+ false, GSI_CONTINUE_LINKING);
+ VEC_replace (tree, args_to_pass, i, arg);
+ }
call = gimple_build_call_vec (node->decl, args_to_pass);
gimple_set_block (call, DECL_INITIAL (current_function_decl));
+ VEC_free (tree, heap, args_to_pass);
/* We avoid address being taken on any variable used by split part,
so return slot optimization is always possible. Moreover this is
gimple_return_set_retval (gsi_stmt (bsi), retval);
break;
}
- else if (gimple_code (gsi_stmt (bsi)) == GIMPLE_ASSIGN)
+ else if (gimple_code (gsi_stmt (bsi)) == GIMPLE_ASSIGN
+ && !gimple_clobber_p (gsi_stmt (bsi)))
{
gimple_assign_set_rhs1 (gsi_stmt (bsi), retval);
break;
}
}
if (DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
- gimple_call_set_lhs (call, build_simple_mem_ref (retval));
+ {
+ gimple_call_set_lhs (call, build_simple_mem_ref (retval));
+ gsi_insert_after (&gsi, call, GSI_NEW_STMT);
+ }
else
- gimple_call_set_lhs (call, retval);
+ {
+ tree restype;
+ restype = TREE_TYPE (DECL_RESULT (current_function_decl));
+ gsi_insert_after (&gsi, call, GSI_NEW_STMT);
+ if (!useless_type_conversion_p (TREE_TYPE (retval), restype))
+ {
+ gimple cpy;
+ tree tem = create_tmp_reg (restype, NULL);
+ tem = make_ssa_name (tem, call);
+ cpy = gimple_build_assign_with_ops (NOP_EXPR, retval,
+ tem, NULL_TREE);
+ gsi_insert_after (&gsi, cpy, GSI_NEW_STMT);
+ retval = tem;
+ }
+ gimple_call_set_lhs (call, retval);
+ update_stmt (call);
+ }
}
- gsi_insert_after (&gsi, call, GSI_NEW_STMT);
+ else
+ gsi_insert_after (&gsi, call, GSI_NEW_STMT);
}
/* We don't use return block (there is either no return in function or
multiple of them). So create new basic block with return statement.
}
free_dominance_info (CDI_DOMINATORS);
free_dominance_info (CDI_POST_DOMINATORS);
- compute_inline_parameters (node);
+ compute_inline_parameters (node, true);
}
/* Execute function splitting pass. */
basic_block bb;
int overall_time = 0, overall_size = 0;
int todo = 0;
- struct cgraph_node *node = cgraph_node (current_function_decl);
+ struct cgraph_node *node = cgraph_get_node (current_function_decl);
- if (flags_from_decl_or_type (current_function_decl) & ECF_NORETURN)
+ if (flags_from_decl_or_type (current_function_decl)
+ & (ECF_NORETURN|ECF_MALLOC))
{
if (dump_file)
- fprintf (dump_file, "Not splitting: noreturn function.\n");
+ fprintf (dump_file, "Not splitting: noreturn/malloc function.\n");
return 0;
}
if (MAIN_NAME_P (DECL_NAME (current_function_decl)))
}
/* This can be relaxed; function might become inlinable after splitting
away the uninlinable part. */
- if (!node->local.inlinable)
+ if (!inline_summary (node)->inlinable)
{
if (dump_file)
fprintf (dump_file, "Not splitting: not inlinable.\n");
return 0;
}
- if (node->local.disregard_inline_limits)
+ if (DECL_DISREGARD_INLINE_LIMITS (node->decl))
{
if (dump_file)
fprintf (dump_file, "Not splitting: disregarding inline limits.\n");
return 0;
}
+ /* Initialize bitmap to track forbidden calls. */
+ forbidden_dominators = BITMAP_ALLOC (NULL);
+ calculate_dominance_info (CDI_DOMINATORS);
+
/* Compute local info about basic blocks and determine function size/time. */
VEC_safe_grow_cleared (bb_info, heap, bb_info_vec, last_basic_block + 1);
memset (&best_split_point, 0, sizeof (best_split_point));
this_time = estimate_num_insns (stmt, &eni_time_weights) * freq;
size += this_size;
time += this_time;
+ check_forbidden_calls (stmt);
if (dump_file && (dump_flags & TDF_DETAILS))
{
BITMAP_FREE (best_split_point.split_bbs);
todo = TODO_update_ssa | TODO_cleanup_cfg;
}
+ BITMAP_FREE (forbidden_dominators);
VEC_free (bb_info, heap, bb_info_vec);
bb_info_vec = NULL;
return todo;
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ TODO_verify_all /* todo_flags_finish */
}
};
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ TODO_verify_all /* todo_flags_finish */
}
};