/* Tree inlining.
- Copyright 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
Contributed by Alexandre Oliva <aoliva@redhat.com>
This file is part of GCC.
void
insert_decl_map (copy_body_data *id, tree key, tree value)
{
- splay_tree_insert (id->decl_map, (splay_tree_key) key,
- (splay_tree_value) value);
+ *pointer_map_insert (id->decl_map, key) = value;
/* Always insert an identity map as well. If we see this same new
node again, we won't want to duplicate it a second time. */
if (key != value)
- splay_tree_insert (id->decl_map, (splay_tree_key) value,
- (splay_tree_value) value);
+ *pointer_map_insert (id->decl_map, value) = value;
}
/* Construct new SSA name for old NAME. ID is the inline context. */
remap_ssa_name (tree name, copy_body_data *id)
{
tree new;
- splay_tree_node n;
+ tree *n;
gcc_assert (TREE_CODE (name) == SSA_NAME);
- n = splay_tree_lookup (id->decl_map, (splay_tree_key) name);
+ n = (tree *) pointer_map_contains (id->decl_map, name);
if (n)
- return (tree) n->value;
+ return *n;
/* Do not set DEF_STMT yet as statement is not copied yet. We do that
in copy_bb. */
tree
remap_decl (tree decl, copy_body_data *id)
{
- splay_tree_node n;
+ tree *n;
tree fn;
/* We only remap local variables in the current function. */
/* See if we have remapped this declaration. */
- n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
+ n = (tree *) pointer_map_contains (id->decl_map, decl);
/* If we didn't already have an equivalent for this declaration,
create one now. */
return t;
}
- return unshare_expr ((tree) n->value);
+ return unshare_expr (*n);
}
static tree
remap_type_1 (tree type, copy_body_data *id)
{
- splay_tree_node node;
+ tree *node;
tree new, t;
if (type == NULL)
return type;
/* See if we have remapped this type. */
- node = splay_tree_lookup (id->decl_map, (splay_tree_key) type);
+ node = (tree *) pointer_map_contains (id->decl_map, type);
if (node)
- return (tree) node->value;
+ return *node;
/* The type only needs remapping if it's variably modified. */
if (! variably_modified_type_p (type, id->src_fn))
tree
remap_type (tree type, copy_body_data *id)
{
- splay_tree_node node;
+ tree *node;
if (type == NULL)
return type;
/* See if we have remapped this type. */
- node = splay_tree_lookup (id->decl_map, (splay_tree_key) type);
+ node = (tree *) pointer_map_contains (id->decl_map, type);
if (node)
- return (tree) node->value;
+ return *node;
/* The type only needs remapping if it's variably modified. */
if (! variably_modified_type_p (type, id->src_fn))
and thus don't count as variable modification. Avoid
keeping bogosities like 0 = 0. */
tree decl = GIMPLE_STMT_OPERAND (*tp, 0), value;
- splay_tree_node n;
+ tree *n;
- n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
+ n = (tree *) pointer_map_contains (id->decl_map, decl);
if (n)
{
- value = (tree) n->value;
+ value = *n;
STRIP_TYPE_NOPS (value);
if (TREE_CONSTANT (value) || TREE_READONLY_DECL_P (value))
{
/* Get rid of *& from inline substitutions that can happen when a
pointer argument is an ADDR_EXPR. */
tree decl = TREE_OPERAND (*tp, 0);
- splay_tree_node n;
+ tree *n;
- n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
+ n = (tree *) pointer_map_contains (id->decl_map, decl);
if (n)
{
tree new;
build_fold_indirect_ref wouldn't strip the INDIRECT_REF,
but we absolutely rely on that. As fold_indirect_ref
does other useful transformations, try that first, though. */
- tree type = TREE_TYPE (TREE_TYPE ((tree)n->value));
- new = unshare_expr ((tree)n->value);
+ tree type = TREE_TYPE (TREE_TYPE (*n));
+ new = unshare_expr (*n);
old = *tp;
*tp = fold_indirect_ref_1 (type, new);
if (! *tp)
new_block = id->block;
if (TREE_BLOCK (*tp))
{
- splay_tree_node n;
- n = splay_tree_lookup (id->decl_map,
- (splay_tree_key) TREE_BLOCK (*tp));
+ tree *n;
+ n = (tree *) pointer_map_contains (id->decl_map,
+ TREE_BLOCK (*tp));
gcc_assert (n);
- new_block = (tree) n->value;
+ new_block = *n;
}
TREE_BLOCK (*tp) = new_block;
}
(NULL_TREE,
id->eh_region_offset + TREE_INT_CST_LOW (TREE_OPERAND (*tp, 0)));
- if (!GIMPLE_TUPLE_P (*tp))
+ if (!GIMPLE_TUPLE_P (*tp) && TREE_CODE (*tp) != OMP_CLAUSE)
TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id);
/* The copied TARGET_EXPR has never been expanded, even if the
copy_basic_block = create_basic_block (NULL, (void *) 0,
(basic_block) bb->prev_bb->aux);
copy_basic_block->count = bb->count * count_scale / REG_BR_PROB_BASE;
- copy_basic_block->frequency = (bb->frequency
+
+ /* We are going to rebuild frequencies from scratch. These values have just
+ small importance to drive canonicalize_loop_headers. */
+ copy_basic_block->frequency = ((gcov_type)bb->frequency
* frequency_scale / REG_BR_PROB_BASE);
+ if (copy_basic_block->frequency > BB_FREQ_MAX)
+ copy_basic_block->frequency = BB_FREQ_MAX;
copy_bsi = bsi_start (copy_basic_block);
for (bsi = bsi_start (bb);
edge = cgraph_edge (id->src_node, orig_stmt);
if (edge)
cgraph_clone_edge (edge, id->dst_node, stmt,
- REG_BR_PROB_BASE, 1, true);
+ REG_BR_PROB_BASE, 1, edge->frequency, true);
break;
case CB_CGE_MOVE_CLONES:
gcc_assert (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt)
!= 0);
- if (tree_could_throw_p (stmt))
+ if (tree_could_throw_p (stmt)
+ /* When we are cloning for inlining, we are supposed to
+ construct a clone that calls precisely the same functions
+ as original. However IPA optimizers might've proved
+ earlier some function calls as non-trapping that might
+ render some basic blocks dead that might become
+ unreachable.
+
+ We can't update SSA with unreachable blocks in CFG and thus
+ we prevent the scenario by preserving even the "dead" eh
+ edges until the point they are later removed by
+ fixup_cfg pass. */
+ || (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES
+ && lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) > 0))
{
int region = lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt);
/* Add an entry for the copied tree in the EH hashtable.
new_cfun->unexpanded_var_list = NULL;
new_cfun->cfg = NULL;
new_cfun->decl = new_fndecl /*= copy_node (callee_fndecl)*/;
- new_cfun->ib_boundaries_block = NULL;
DECL_STRUCT_FUNCTION (new_fndecl) = new_cfun;
push_cfun (new_cfun);
init_empty_tree_cfg ();
new->aux = bb;
}
- last = n_basic_blocks;
+ last = last_basic_block;
/* Now that we've duplicated the blocks, duplicate their edges. */
FOR_ALL_BB_FN (bb, cfun_to_copy)
copy_edges_for_bb (bb, count_scale);
}
/* Zero out AUX fields of newly created block during EH edge
insertion. */
- for (; last < n_basic_blocks; last++)
+ for (; last < last_basic_block; last++)
BASIC_BLOCK (last)->aux = NULL;
entry_block_map->aux = NULL;
exit_block_map->aux = NULL;
if (def && gimple_in_ssa_p (cfun) && is_gimple_reg (p))
{
def = remap_ssa_name (def, id);
- init_stmt = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (var), def, rhs);
+ init_stmt = build_gimple_modify_stmt (def, rhs);
SSA_NAME_DEF_STMT (def) = init_stmt;
SSA_NAME_IS_DEFAULT_DEF (def) = 0;
set_default_def (var, NULL);
}
else
- init_stmt = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (var), var, rhs);
+ init_stmt = build_gimple_modify_stmt (var, rhs);
/* If we did not create a gimple value and we did not create a gimple
cast of a gimple value, then we will need to gimplify INIT_STMTS
}
/* Generate code to initialize the parameters of the function at the
- top of the stack in ID from the ARGS (presented as a TREE_LIST). */
+ top of the stack in ID from the CALL_EXPR EXP. */
static void
-initialize_inlined_parameters (copy_body_data *id, tree args, tree static_chain,
+initialize_inlined_parameters (copy_body_data *id, tree exp,
tree fn, basic_block bb)
{
tree parms;
tree p;
tree vars = NULL_TREE;
int argnum = 0;
+ call_expr_arg_iterator iter;
+ tree static_chain = CALL_EXPR_STATIC_CHAIN (exp);
/* Figure out what the parameters are. */
parms = DECL_ARGUMENTS (fn);
/* Loop through the parameter declarations, replacing each with an
equivalent VAR_DECL, appropriately initialized. */
- for (p = parms, a = args; p;
- a = a ? TREE_CHAIN (a) : a, p = TREE_CHAIN (p))
+ for (p = parms, a = first_call_expr_arg (exp, &iter); p;
+ a = next_call_expr_arg (&iter), p = TREE_CHAIN (p))
{
tree value;
/* Find the initializer. */
value = lang_hooks.tree_inlining.convert_parm_for_inlining
- (p, a ? TREE_VALUE (a) : NULL_TREE, fn, argnum);
+ (p, a, fn, argnum);
setup_one_parameter (id, p, value, fn, bb, &vars);
}
case VEC_WIDEN_MULT_LO_EXPR:
case VEC_UNPACK_HI_EXPR:
case VEC_UNPACK_LO_EXPR:
- case VEC_PACK_MOD_EXPR:
+ case VEC_UNPACK_FLOAT_HI_EXPR:
+ case VEC_UNPACK_FLOAT_LO_EXPR:
+ case VEC_PACK_TRUNC_EXPR:
case VEC_PACK_SAT_EXPR:
+ case VEC_PACK_FIX_TRUNC_EXPR:
case WIDEN_MULT_EXPR:
case CALL_EXPR:
{
tree decl = get_callee_fndecl (x);
- tree arg;
cost = d->weights->call_cost;
if (decl && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
that does use function declaration to figure out the arguments. */
if (!decl)
{
- for (arg = TREE_OPERAND (x, 1); arg; arg = TREE_CHAIN (arg))
- d->count += estimate_move_cost (TREE_TYPE (TREE_VALUE (arg)));
+ tree a;
+ call_expr_arg_iterator iter;
+ FOR_EACH_CALL_EXPR_ARG (a, iter, x)
+ d->count += estimate_move_cost (TREE_TYPE (a));
}
else
{
+ tree arg;
for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
d->count += estimate_move_cost (TREE_TYPE (arg));
}
/* Estimating time for call is difficult, since we have no idea what the
called function does. In the current uses of eni_time_weights,
underestimating the cost does less harm than overestimating it, so
- we choose a rather small walue here. */
+ we choose a rather small value here. */
eni_time_weights.call_cost = 10;
eni_time_weights.div_mod_cost = 10;
eni_time_weights.switch_cost = 4;
tree t;
tree use_retvar;
tree fn;
- splay_tree st;
- tree args;
+ struct pointer_map_t *st;
tree return_slot;
tree modify_dest;
location_t saved_location;
(incorrect node sharing is most common reason for missing edges. */
gcc_assert (dest->needed || !flag_unit_at_a_time);
cgraph_create_edge (id->dst_node, dest, stmt,
- bb->count, bb->loop_depth)->inline_failed
+ bb->count, CGRAPH_FREQ_BASE,
+ bb->loop_depth)->inline_failed
= N_("originally indirect function call not considered for inlining");
+ if (dump_file)
+ {
+ fprintf (dump_file, "Created new direct edge to %s",
+ cgraph_node_name (dest));
+ }
goto egress;
}
/* Local declarations will be replaced by their equivalents in this
map. */
st = id->decl_map;
- id->decl_map = splay_tree_new (splay_tree_compare_pointers,
- NULL, NULL);
-
- /* Initialize the parameters. */
- args = TREE_OPERAND (t, 1);
+ id->decl_map = pointer_map_create ();
/* Record the function we are about to inline. */
id->src_fn = fn;
id->src_node = cg_edge->callee;
id->src_cfun = DECL_STRUCT_FUNCTION (fn);
- initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb);
+ initialize_inlined_parameters (id, t, fn, bb);
if (DECL_INITIAL (fn))
add_lexical_block (id->block, remap_blocks (DECL_INITIAL (fn), id));
}
/* Clean up. */
- splay_tree_delete (id->decl_map);
+ pointer_map_destroy (id->decl_map);
id->decl_map = st;
/* If the inlined function returns a result that we care about,
}
}
+/* Return true if BB has at least one abnormal outgoing edge. */
+
+static inline bool
+has_abnormal_outgoing_edge_p (basic_block bb)
+{
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->flags & EDGE_ABNORMAL)
+ return true;
+
+ return false;
+}
+
+/* When a block from the inlined function contains a call with side-effects
+ in the middle gets inlined in a function with non-locals labels, the call
+ becomes a potential non-local goto so we need to add appropriate edge. */
+
+static void
+make_nonlocal_label_edges (void)
+{
+ block_stmt_iterator bsi;
+ basic_block bb;
+
+ FOR_EACH_BB (bb)
+ {
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ tree stmt = bsi_stmt (bsi);
+ if (tree_can_make_abnormal_goto (stmt))
+ {
+ if (stmt == bsi_stmt (bsi_last (bb)))
+ {
+ if (!has_abnormal_outgoing_edge_p (bb))
+ make_abnormal_goto_edges (bb, true);
+ }
+ else
+ {
+ edge e = split_block (bb, stmt);
+ bb = e->src;
+ make_abnormal_goto_edges (bb, true);
+ }
+ break;
+ }
+
+ /* Update PHIs on nonlocal goto receivers we (possibly)
+ just created new edges into. */
+ if (TREE_CODE (stmt) == LABEL_EXPR
+ && gimple_in_ssa_p (cfun))
+ {
+ tree target = LABEL_EXPR_LABEL (stmt);
+ if (DECL_NONLOCAL (target))
+ {
+ tree phi;
+
+ for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+ {
+ gcc_assert (SSA_NAME_OCCURS_IN_ABNORMAL_PHI
+ (PHI_RESULT (phi)));
+ mark_sym_for_renaming
+ (SSA_NAME_VAR (PHI_RESULT (phi)));
+ }
+ }
+ }
+ }
+ }
+}
+
/* Expand calls to inline functions in the body of FN. */
unsigned int
push_gimplify_context ();
+ /* We make no attempts to keep dominance info up-to-date. */
+ free_dominance_info (CDI_DOMINATORS);
+ free_dominance_info (CDI_POST_DOMINATORS);
+
/* Reach the trees by walking over the CFG, and note the
enclosing basic-blocks in the call edges. */
/* We walk the blocks going forward, because inlined function bodies
gcc_assert (e->inline_failed);
}
#endif
- /* We need to rescale frequencies again to peak at REG_BR_PROB_BASE
- as inlining loops might increase the maximum. */
- if (ENTRY_BLOCK_PTR->count)
- counts_to_freqs ();
/* We are not going to maintain the cgraph edges up to date.
Kill it so it won't confuse us. */
fold_marked_statements (last, id.statements_to_fold);
pointer_set_destroy (id.statements_to_fold);
fold_cond_expr_cond ();
- /* We make no attempts to keep dominance info up-to-date. */
- free_dominance_info (CDI_DOMINATORS);
- free_dominance_info (CDI_POST_DOMINATORS);
+ if (current_function_has_nonlocal_label)
+ make_nonlocal_label_edges ();
/* It would be nice to check SSA/CFG/statement consistency here, but it is
not possible yet - the IPA passes might make various functions to not
throw and they don't care to proactively update local EH info. This is
done later in fixup_cfg pass that also execute the verification. */
return (TODO_update_ssa | TODO_cleanup_cfg
- | (gimple_in_ssa_p (cfun) ? TODO_remove_unused_locals : 0));
+ | (gimple_in_ssa_p (cfun) ? TODO_remove_unused_locals : 0)
+ | (profile_status != PROFILE_ABSENT ? TODO_rebuild_frequencies : 0));
}
/* FN is a function that has a complete body, and CLONE is a function whose
id.src_fn = fn;
id.dst_fn = clone;
id.src_cfun = DECL_STRUCT_FUNCTION (fn);
- id.decl_map = (splay_tree)arg_map;
+ id.decl_map = (struct pointer_map_t *)arg_map;
id.copy_decl = copy_decl_no_change;
id.transform_call_graph_edges = CB_CGE_DUPLICATE;
static void
remap_save_expr (tree *tp, void *st_, int *walk_subtrees)
{
- splay_tree st = (splay_tree) st_;
- splay_tree_node n;
+ struct pointer_map_t *st = (struct pointer_map_t *) st_;
+ tree *n;
tree t;
/* See if we already encountered this SAVE_EXPR. */
- n = splay_tree_lookup (st, (splay_tree_key) *tp);
+ n = (tree *) pointer_map_contains (st, *tp);
/* If we didn't already remap this SAVE_EXPR, do so now. */
if (!n)
t = copy_node (*tp);
/* Remember this SAVE_EXPR. */
- splay_tree_insert (st, (splay_tree_key) *tp, (splay_tree_value) t);
+ *pointer_map_insert (st, *tp) = t;
/* Make sure we don't remap an already-remapped SAVE_EXPR. */
- splay_tree_insert (st, (splay_tree_key) t, (splay_tree_value) t);
+ *pointer_map_insert (st, t) = t;
}
else
{
/* We've already walked into this SAVE_EXPR; don't do it again. */
*walk_subtrees = 0;
- t = (tree) n->value;
+ t = *n;
}
/* Replace this SAVE_EXPR with the copy. */
unsave_r (tree *tp, int *walk_subtrees, void *data)
{
copy_body_data *id = (copy_body_data *) data;
- splay_tree st = id->decl_map;
- splay_tree_node n;
+ struct pointer_map_t *st = id->decl_map;
+ tree *n;
/* Only a local declaration (variable or label). */
if ((TREE_CODE (*tp) == VAR_DECL && !TREE_STATIC (*tp))
|| TREE_CODE (*tp) == LABEL_DECL)
{
/* Lookup the declaration. */
- n = splay_tree_lookup (st, (splay_tree_key) *tp);
+ n = (tree *) pointer_map_contains (st, *tp);
/* If it's there, remap it. */
if (n)
- *tp = (tree) n->value;
+ *tp = *n;
}
else if (TREE_CODE (*tp) == STATEMENT_LIST)
memset (&id, 0, sizeof (id));
id.src_fn = current_function_decl;
id.dst_fn = current_function_decl;
- id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
+ id.decl_map = pointer_map_create ();
id.copy_decl = copy_decl_no_change;
id.transform_call_graph_edges = CB_CGE_DUPLICATE;
walk_tree (&expr, unsave_r, &id, NULL);
/* Clean up. */
- splay_tree_delete (id.decl_map);
+ pointer_map_destroy (id.decl_map);
return expr;
}
id.statements_to_fold = pointer_set_create ();
}
- id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
+ id.decl_map = pointer_map_create ();
id.src_fn = old_decl;
id.dst_fn = new_decl;
id.src_node = old_version_node;
number_blocks (new_decl);
/* Clean up. */
- splay_tree_delete (id.decl_map);
+ pointer_map_destroy (id.decl_map);
if (!update_clones)
{
fold_marked_statements (0, id.statements_to_fold);
id.src_fn = current_function_decl;
id.dst_fn = current_function_decl;
id.src_cfun = cfun;
- id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
+ id.decl_map = pointer_map_create ();
type = remap_type_1 (type, &id);
- splay_tree_delete (id.decl_map);
+ pointer_map_destroy (id.decl_map);
return type;
}