/* Interprocedural constant propagation
- Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Free Software Foundation, Inc.
Contributed by Razya Ladelsky <RAZYA@il.ibm.com> and Martin Jambor
static inline struct ipcp_lattice *
ipa_get_lattice (struct ipa_node_params *info, int i)
{
- gcc_assert (i >= 0 && i <= ipa_get_param_count (info));
+ gcc_assert (i >= 0 && i < ipa_get_param_count (info));
gcc_checking_assert (!info->ipcp_orig_node);
gcc_checking_assert (info->lattices);
return &(info->lattices[i]);
static void
determine_versionability (struct cgraph_node *node)
{
- struct cgraph_edge *edge;
const char *reason = NULL;
/* There are a number of generic reasons functions cannot be versioned. We
present. */
if (node->alias || node->thunk.thunk_p)
reason = "alias or thunk";
- else if (!inline_summary (node)->versionable)
- reason = "inliner claims it is so";
- else if (TYPE_ATTRIBUTES (TREE_TYPE (node->decl)))
- reason = "there are type attributes";
+ else if (!node->local.versionable)
+ reason = "not a tree_versionable_function";
else if (cgraph_function_body_availability (node) <= AVAIL_OVERWRITABLE)
reason = "insufficient body availability";
- else
- /* Removing arguments doesn't work if the function takes varargs
- or use __builtin_apply_args.
- FIXME: handle this together with can_change_signature flag. */
- for (edge = node->callees; edge; edge = edge->next_callee)
- {
- tree t = edge->callee->decl;
- if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (t) == BUILT_IN_APPLY_ARGS
- || DECL_FUNCTION_CODE (t) == BUILT_IN_VA_START))
- {
- reason = "prohibitive builtins called";
- break;
- };
- }
if (reason && dump_file && !node->alias && !node->thunk.thunk_p)
fprintf (dump_file, "Function %s/%i is not versionable, reason: %s.\n",
cgraph_node_name (node), node->uid, reason);
- IPA_NODE_REF (node)->node_versionable = (reason == NULL);
+ node->local.versionable = (reason == NULL);
}
/* Return true if it is at all technically possible to create clones of a
static bool
ipcp_versionable_function_p (struct cgraph_node *node)
{
- return IPA_NODE_REF (node)->node_versionable;
+ return node->local.versionable;
}
/* Structure holding accumulated information about callers of a node. */
int i;
gcc_checking_assert (cgraph_function_with_gimple_body_p (node));
- if (ipa_is_called_with_var_arguments (info))
- disable = true;
- else if (!node->local.local)
+ if (!node->local.local)
{
/* When cloning is allowed, we can assume that externally visible
functions are not called. We will compensate this by cloning
return NULL_TREE;
}
+/* Extract the acual BINFO being described by JFUNC which must be a known type
+ jump function. */
+
+static tree
+ipa_value_from_known_type_jfunc (struct ipa_jump_func *jfunc)
+{
+ tree base_binfo = TYPE_BINFO (jfunc->value.known_type.base_type);
+ if (!base_binfo)
+ return NULL_TREE;
+ return get_binfo_at_offset (base_binfo,
+ jfunc->value.known_type.offset,
+ jfunc->value.known_type.component_type);
+}
+
/* Determine whether JFUNC evaluates to a known value (that is either a
constant or a binfo) and if so, return it. Otherwise return NULL. INFO
describes the caller node so that pass-through jump functions can be
evaluated. */
-static tree
+tree
ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc)
{
if (jfunc->type == IPA_JF_CONST)
return jfunc->value.constant;
else if (jfunc->type == IPA_JF_KNOWN_TYPE)
- return jfunc->value.base_binfo;
+ return ipa_value_from_known_type_jfunc (jfunc);
else if (jfunc->type == IPA_JF_PASS_THROUGH
|| jfunc->type == IPA_JF_ANCESTOR)
{
return NULL_TREE;
}
-/* Determine whether JFUNC evaluates to a constant and if so, return it.
- Otherwise return NULL. INFO describes the caller node so that pass-through
- jump functions can be evaluated. */
-
-tree
-ipa_cst_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc)
-{
- tree res = ipa_value_from_jfunc (info, jfunc);
-
- if (res && TREE_CODE (res) == TREE_BINFO)
- return NULL_TREE;
- else
- return res;
-}
-
/* If checking is enabled, verify that no lattice is in the TOP state, i.e. not
bottom, not containing a variable component and without any known value at
tree val;
if (jfunc->type == IPA_JF_KNOWN_TYPE)
- val = jfunc->value.base_binfo;
+ {
+ val = ipa_value_from_known_type_jfunc (jfunc);
+ if (!val)
+ return set_lattice_contains_variable (dest_lat);
+ }
else
val = jfunc->value.constant;
return add_value_to_lattice (dest_lat, val, cs, NULL, 0);
struct cgraph_node *callee, *alias_or_thunk;
struct ipa_edge_args *args;
bool ret = false;
- int i, count;
+ int i, args_count, parms_count;
callee = cgraph_function_node (cs->callee, &availability);
if (!callee->analyzed)
return false;
gcc_checking_assert (cgraph_function_with_gimple_body_p (callee));
callee_info = IPA_NODE_REF (callee);
- if (ipa_is_called_with_var_arguments (callee_info))
- return false;
args = IPA_EDGE_REF (cs);
- count = ipa_get_cs_argument_count (args);
+ args_count = ipa_get_cs_argument_count (args);
+ parms_count = ipa_get_param_count (callee_info);
/* If this call goes through a thunk we must not propagate to the first (0th)
parameter. However, we might need to uncover a thunk from below a series
else
i = 0;
- for (; i < count; i++)
+ for (; (i < args_count) && (i < parms_count); i++)
{
struct ipa_jump_func *jump_func = ipa_get_ith_jump_func (args, i);
struct ipcp_lattice *dest_lat = ipa_get_lattice (callee_info, i);
else
ret |= propagate_accross_jump_function (cs, jump_func, dest_lat);
}
+ for (; i < parms_count; i++)
+ ret |= set_lattice_contains_variable (ipa_get_lattice (callee_info, i));
+
return ret;
}
/* If an indirect edge IE can be turned into a direct one based on KNOWN_VALS
(which can contain both constants and binfos) or KNOWN_BINFOS (which can be
- NULL) return the destination. If simple thunk delta must be applied too,
- store it to DELTA. */
+ NULL) return the destination. */
-static tree
-get_indirect_edge_target (struct cgraph_edge *ie, tree *delta,
- VEC (tree, heap) *known_vals,
- VEC (tree, heap) *known_binfos)
+tree
+ipa_get_indirect_edge_target (struct cgraph_edge *ie,
+ VEC (tree, heap) *known_vals,
+ VEC (tree, heap) *known_binfos)
{
int param_index = ie->indirect_info->param_index;
HOST_WIDE_INT token, anc_offset;
if (!ie->indirect_info->polymorphic)
{
- tree t = VEC_index (tree, known_vals, param_index);
+ tree t = (VEC_length (tree, known_vals) > (unsigned int) param_index
+ ? VEC_index (tree, known_vals, param_index) : NULL);
if (t &&
TREE_CODE (t) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL)
- {
- *delta = NULL_TREE;
- return TREE_OPERAND (t, 0);
- }
+ return TREE_OPERAND (t, 0);
else
return NULL_TREE;
}
otr_type = ie->indirect_info->otr_type;
t = VEC_index (tree, known_vals, param_index);
- if (!t && known_binfos)
+ if (!t && known_binfos
+ && VEC_length (tree, known_binfos) > (unsigned int) param_index)
t = VEC_index (tree, known_binfos, param_index);
if (!t)
return NULL_TREE;
binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
if (!binfo)
return NULL_TREE;
- return gimple_get_virt_method_for_binfo (token, binfo, delta);
+ return gimple_get_virt_method_for_binfo (token, binfo);
}
else
{
binfo = get_binfo_at_offset (t, anc_offset, otr_type);
if (!binfo)
return NULL_TREE;
- return gimple_get_virt_method_for_binfo (token, binfo, delta);
+ return gimple_get_virt_method_for_binfo (token, binfo);
}
}
{
struct cgraph_node *callee;
struct inline_summary *isummary;
- tree delta, target;
+ tree target;
- target = get_indirect_edge_target (ie, &delta, known_csts, known_binfos);
+ target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos);
if (!target)
continue;
|| !optimize_function_for_speed_p (DECL_STRUCT_FUNCTION (node->decl)))
return false;
- gcc_checking_assert (size_cost >= 0);
+ gcc_assert (size_cost > 0);
- /* FIXME: These decisions need tuning. */
if (max_count)
{
- int evaluation, factor = (count_sum * 1000) / max_count;
-
- evaluation = (time_benefit * factor) / size_cost;
+ int factor = (count_sum * 1000) / max_count;
+ HOST_WIDEST_INT evaluation = (((HOST_WIDEST_INT) time_benefit * factor)
+ / size_cost);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " good_cloning_opportunity_p (time: %i, "
"size: %i, count_sum: " HOST_WIDE_INT_PRINT_DEC
- ") -> evaluation: %i, threshold: %i\n",
+ ") -> evaluation: " HOST_WIDEST_INT_PRINT_DEC
+ ", threshold: %i\n",
time_benefit, size_cost, (HOST_WIDE_INT) count_sum,
evaluation, 500);
}
else
{
- int evaluation = (time_benefit * freq_sum) / size_cost;
+ HOST_WIDEST_INT evaluation = (((HOST_WIDEST_INT) time_benefit * freq_sum)
+ / size_cost);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " good_cloning_opportunity_p (time: %i, "
- "size: %i, freq_sum: %i) -> evaluation: %i, threshold: %i\n",
+ "size: %i, freq_sum: %i) -> evaluation: "
+ HOST_WIDEST_INT_PRINT_DEC ", threshold: %i\n",
time_benefit, size_cost, freq_sum, evaluation,
CGRAPH_FREQ_BASE /2);
init_caller_stats (&stats);
cgraph_for_node_and_aliases (node, gather_caller_stats, &stats, false);
- estimate_ipcp_clone_size_and_time (node, known_csts, &size, &time);
+ estimate_ipcp_clone_size_and_time (node, known_csts, known_binfos,
+ &size, &time);
time -= devirtualization_time_bonus (node, known_csts, known_binfos);
time -= removable_params_cost;
size -= stats.n_calls * removable_params_cost;
else
continue;
- estimate_ipcp_clone_size_and_time (node, known_csts, &size, &time);
+ estimate_ipcp_clone_size_and_time (node, known_csts, known_binfos,
+ &size, &time);
time_benefit = base_time - time
+ devirtualization_time_bonus (node, known_csts, known_binfos)
+ removable_params_cost + emc;
+ gcc_checking_assert (size >=0);
+ /* The inliner-heuristics based estimates may think that in certain
+ contexts some functions do not have any size at all but we want
+ all specializations to have at least a tiny cost, not least not to
+ divide by zero. */
+ if (size == 0)
+ size = 1;
+
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, " - estimates for value ");
}
}
+
+/* Return the sum of A and B if none of them is bigger than INT_MAX/2, return
+ the bigger one if otherwise. */
+
+static int
+safe_add (int a, int b)
+{
+ if (a > INT_MAX/2 || b > INT_MAX/2)
+ return a > b ? a : b;
+ else
+ return a + b;
+}
+
+
/* Propagate the estimated effects of individual values along the topological
from the dependant values to those they depend on. */
for (val = base; val; val = val->scc_next)
{
- time += val->local_time_benefit + val->prop_time_benefit;
- size += val->local_size_cost + val->prop_size_cost;
+ time = safe_add (time,
+ val->local_time_benefit + val->prop_time_benefit);
+ size = safe_add (size, val->local_size_cost + val->prop_size_cost);
}
for (val = base; val; val = val->scc_next)
if (src->val
&& cgraph_maybe_hot_edge_p (src->cs))
{
- src->val->prop_time_benefit += time;
- src->val->prop_size_cost += size;
+ src->val->prop_time_benefit = safe_add (time,
+ src->val->prop_time_benefit);
+ src->val->prop_size_cost = safe_add (size,
+ src->val->prop_size_cost);
}
}
}
for (ie = node->indirect_calls; ie; ie = next_ie)
{
- tree delta, target;
+ tree target;
next_ie = ie->next_callee;
- target = get_indirect_edge_target (ie, &delta, known_vals, NULL);
+ target = ipa_get_indirect_edge_target (ie, known_vals, NULL);
if (target)
- ipa_make_edge_direct_to_target (ie, target, delta);
+ ipa_make_edge_direct_to_target (ie, target);
}
}
}
}
else
- args_to_skip = NULL;
+ {
+ args_to_skip = NULL;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " cannot change function signature\n");
+ }
for (i = 0; i < count ; i++)
{
struct ipa_jump_func *jump_func;
tree t;
+ if (i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs)))
+ {
+ newval = NULL_TREE;
+ break;
+ }
jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
-
t = ipa_value_from_jfunc (IPA_NODE_REF (cs->caller), jump_func);
if (!t
|| (newval
if (!val)
continue;
+ if (i >= ipa_get_cs_argument_count (args))
+ {
+ insufficient = true;
+ break;
+ }
jump_func = ipa_get_ith_jump_func (args, i);
t = ipa_value_from_jfunc (caller_info, jump_func);
if (!t || !values_equal_for_ipcp_p (val, t))
if (dump_file)
fprintf (dump_file, " - adding an extra caller %s/%i"
" of %s/%i\n",
- cgraph_node_name (cs->caller), cs->caller->uid,
- cgraph_node_name (val->spec_node),
+ xstrdup (cgraph_node_name (cs->caller)),
+ cs->caller->uid,
+ xstrdup (cgraph_node_name (val->spec_node)),
val->spec_node->uid);
cgraph_redirect_edge_callee (cs, val->spec_node);
fprintf (dump_file, "\nIPA constant propagation start:\n");
ipa_register_cgraph_hooks ();
- /* FIXME: We could propagate through thunks happily and we could be
- even able to clone them, if needed. Do that later. */
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
/* Unreachable nodes should have been eliminated before ipcp. */
gcc_assert (node->needed || node->reachable);
-
- inline_summary (node)->versionable = tree_versionable_function_p (node->decl);
+ node->local.versionable = tree_versionable_function_p (node->decl);
ipa_analyze_node (node);
}
}