/* Interprocedural constant propagation
- Copyright (C) 2005-2015 Free Software Foundation, Inc.
+ Copyright (C) 2005-2016 Free Software Foundation, Inc.
Contributed by Razya Ladelsky <RAZYA@il.ibm.com> and Martin Jambor
<mjambor@suse.cz>
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "vec.h"
-#include "hash-map.h"
-#include "double-int.h"
-#include "input.h"
-#include "alias.h"
-#include "symtab.h"
-#include "options.h"
-#include "wide-int.h"
-#include "inchash.h"
+#include "backend.h"
#include "tree.h"
-#include "fold-const.h"
-#include "gimple-fold.h"
#include "gimple-expr.h"
-#include "target.h"
#include "predict.h"
-#include "basic-block.h"
-#include "is-a.h"
-#include "plugin-api.h"
-#include "tm.h"
-#include "hard-reg-set.h"
-#include "input.h"
-#include "function.h"
-#include "ipa-ref.h"
-#include "cgraph.h"
#include "alloc-pool.h"
-#include "symbol-summary.h"
-#include "ipa-prop.h"
-#include "bitmap.h"
#include "tree-pass.h"
-#include "flags.h"
+#include "cgraph.h"
#include "diagnostic.h"
+#include "fold-const.h"
+#include "gimple-fold.h"
+#include "symbol-summary.h"
+#include "ipa-prop.h"
#include "tree-pretty-print.h"
#include "tree-inline.h"
#include "params.h"
struct ipcp_agg_lattice *next;
};
+/* Lattice of pointer alignment. Unlike the previous types of lattices, this
+ one is only capable of holding one value. */
+
+class ipcp_alignment_lattice
+{
+public:
+ /* If bottom and top are both false, these two fields hold values as given by
+ ptr_info_def and get_pointer_alignment_1. */
+ unsigned align;
+ unsigned misalign;
+
+ inline bool bottom_p () const;
+ inline bool top_p () const;
+ inline bool set_to_bottom ();
+ bool meet_with (unsigned new_align, unsigned new_misalign);
+ bool meet_with (const ipcp_alignment_lattice &other, HOST_WIDE_INT offset);
+ void print (FILE * f);
+private:
+ /* If set, this lattice is bottom and all other fields should be
+ disregarded. */
+ bool bottom;
+ /* If bottom and not_top are false, the lattice is TOP. If not_top is true,
+ the known alignment is stored in the fields align and misalign. The field
+ is negated so that memset to zero initializes the lattice to TOP
+ state. */
+ bool not_top;
+
+ bool meet_with_1 (unsigned new_align, unsigned new_misalign);
+};
+
/* Structure containing lattices for a parameter itself and for pieces of
aggregates that are passed in the parameter or by a reference in a parameter
plus some other useful flags. */
public:
/* Lattice describing the value of the parameter itself. */
ipcp_lattice<tree> itself;
- /* Lattice describing the the polymorphic contexts of a parameter. */
+ /* Lattice describing the polymorphic contexts of a parameter. */
ipcp_lattice<ipa_polymorphic_call_context> ctxlat;
/* Lattices describing aggregate parts. */
ipcp_agg_lattice *aggs;
- /* Alignment information. Very basic one value lattice where !known means
- TOP and zero alignment bottom. */
- ipa_alignment alignment;
+ /* Lattice describing known alignment. */
+ ipcp_alignment_lattice alignment;
/* Number of aggregate lattices */
int aggs_count;
/* True if aggregate data were passed by reference (as opposed to by
/* Allocation pools for values and their sources in ipa-cp. */
-alloc_pool ipcp_cst_values_pool;
-alloc_pool ipcp_poly_ctx_values_pool;
-alloc_pool ipcp_sources_pool;
-alloc_pool ipcp_agg_lattice_pool;
+object_allocator<ipcp_value<tree> > ipcp_cst_values_pool
+ ("IPA-CP constant values");
+
+object_allocator<ipcp_value<ipa_polymorphic_call_context> >
+ ipcp_poly_ctx_values_pool ("IPA-CP polymorphic contexts");
+
+object_allocator<ipcp_value_source<tree> > ipcp_sources_pool
+ ("IPA-CP value sources");
+
+object_allocator<ipcp_agg_lattice> ipcp_agg_lattice_pool
+ ("IPA_CP aggregate lattices");
/* Maximal count found in program. */
fprintf (f, "\n");
}
+/* Print alignment lattice to F. */
+
+void
+ipcp_alignment_lattice::print (FILE * f)
+{
+ if (top_p ())
+ fprintf (f, " Alignment unknown (TOP)\n");
+ else if (bottom_p ())
+ fprintf (f, " Alignment unusable (BOTTOM)\n");
+ else
+ fprintf (f, " Alignment %u, misalignment %u\n", align, misalign);
+}
+
/* Print all ipcp_lattices of all functions to F. */
static void
plats->itself.print (f, dump_sources, dump_benefits);
fprintf (f, " ctxs: ");
plats->ctxlat.print (f, dump_sources, dump_benefits);
- if (plats->alignment.known && plats->alignment.align > 0)
- fprintf (f, " Alignment %u, misalignment %u\n",
- plats->alignment.align, plats->alignment.misalign);
- else if (plats->alignment.known)
- fprintf (f, " Alignment unusable\n");
- else
- fprintf (f, " Alignment unknown\n");
+ plats->alignment.print (f);
if (plats->virt_call)
fprintf (f, " virt_call flag set\n");
with NODE. */
static void
-determine_versionability (struct cgraph_node *node)
+determine_versionability (struct cgraph_node *node,
+ struct ipa_node_params *info)
{
const char *reason = NULL;
fprintf (dump_file, "Function %s/%i is not versionable, reason: %s.\n",
node->name (), node->order, reason);
- node->local.versionable = (reason == NULL);
+ info->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 node->local.versionable;
+ return IPA_NODE_REF (node)->versionable;
}
/* Structure holding accumulated information about callers of a node. */
struct cgraph_edge *cs;
for (cs = node->callers; cs; cs = cs->next_caller)
- if (cs->caller->thunk.thunk_p)
- cs->caller->call_for_symbol_thunks_and_aliases (gather_caller_stats,
- stats, false);
- else
+ if (!cs->caller->thunk.thunk_p)
{
stats->count_sum += cs->count;
stats->freq_sum += cs->frequency;
return false;
}
- if (!optimize_function_for_speed_p (DECL_STRUCT_FUNCTION (node->decl)))
+ if (node->optimize_for_size_p ())
{
if (dump_file)
fprintf (dump_file, "Not considering %s for cloning; "
return ret;
}
-/* Return true if alignment information in PLATS is known to be unusable. */
+/* Return true if alignment information in the lattice is yet unknown. */
-static inline bool
-alignment_bottom_p (ipcp_param_lattices *plats)
+bool
+ipcp_alignment_lattice::top_p () const
{
- return plats->alignment.known && (plats->alignment.align == 0);
+ return !bottom && !not_top;
}
-/* Set alignment information in PLATS to unusable. Return true if it
- previously was usable or unknown. */
+/* Return true if alignment information in the lattice is known to be
+ unusable. */
-static inline bool
-set_alignment_to_bottom (ipcp_param_lattices *plats)
+bool
+ipcp_alignment_lattice::bottom_p () const
+{
+ return bottom;
+}
+
+/* Set alignment information in the lattice to bottom. Return true if it
+ previously was in a different state. */
+
+bool
+ipcp_alignment_lattice::set_to_bottom ()
{
- if (alignment_bottom_p (plats))
+ if (bottom_p ())
return false;
- plats->alignment.known = true;
- plats->alignment.align = 0;
+ bottom = true;
return true;
}
+/* Meet the current value of the lattice with alignment described by NEW_ALIGN
+ and NEW_MISALIGN, assuming that we know the current value is neither TOP nor
+ BOTTOM. Return true if the value of lattice has changed. */
+
+bool
+ipcp_alignment_lattice::meet_with_1 (unsigned new_align, unsigned new_misalign)
+{
+ gcc_checking_assert (new_align != 0);
+ if (align == new_align && misalign == new_misalign)
+ return false;
+
+ bool changed = false;
+ if (align > new_align)
+ {
+ align = new_align;
+ misalign = misalign % new_align;
+ changed = true;
+ }
+ if (misalign != (new_misalign % align))
+ {
+ int diff = abs ((int) misalign - (int) (new_misalign % align));
+ align = (unsigned) diff & -diff;
+ if (align)
+ misalign = misalign % align;
+ else
+ set_to_bottom ();
+ changed = true;
+ }
+ gcc_checking_assert (bottom_p () || align != 0);
+ return changed;
+}
+
+/* Meet the current value of the lattice with alignment described by NEW_ALIGN
+ and NEW_MISALIGN. Return true if the value of lattice has changed. */
+
+bool
+ipcp_alignment_lattice::meet_with (unsigned new_align, unsigned new_misalign)
+{
+ gcc_assert (new_align != 0);
+ if (bottom_p ())
+ return false;
+ if (top_p ())
+ {
+ not_top = true;
+ align = new_align;
+ misalign = new_misalign;
+ return true;
+ }
+ return meet_with_1 (new_align, new_misalign);
+}
+
+/* Meet the current value of the lattice with OTHER, taking into account that
+ OFFSET has been added to the pointer value. Return true if the value of
+ lattice has changed. */
+
+bool
+ipcp_alignment_lattice::meet_with (const ipcp_alignment_lattice &other,
+ HOST_WIDE_INT offset)
+{
+ if (other.bottom_p ())
+ return set_to_bottom ();
+ if (bottom_p () || other.top_p ())
+ return false;
+
+ unsigned adjusted_misalign = (other.misalign + offset) % other.align;
+ if (top_p ())
+ {
+ not_top = true;
+ align = other.align;
+ misalign = adjusted_misalign;
+ return true;
+ }
+
+ return meet_with_1 (other.align, adjusted_misalign);
+}
+
/* Mark bot aggregate and scalar lattices as containing an unknown variable,
return true is any of them has not been marked as such so far. */
ret = plats->itself.set_contains_variable ();
ret |= plats->ctxlat.set_contains_variable ();
ret |= set_agg_lats_contain_variable (plats);
- ret |= set_alignment_to_bottom (plats);
+ ret |= plats->alignment.set_to_bottom ();
return ret;
}
+/* Worker of call_for_symbol_thunks_and_aliases, increment the integer DATA
+ points to by the number of callers to NODE. */
+
+static bool
+count_callers (cgraph_node *node, void *data)
+{
+ int *caller_count = (int *) data;
+
+ for (cgraph_edge *cs = node->callers; cs; cs = cs->next_caller)
+ /* Local thunks can be handled transparently, but if the thunk can not
+ be optimized out, count it as a real use. */
+ if (!cs->caller->thunk.thunk_p || !cs->caller->local.local)
+ ++*caller_count;
+ return false;
+}
+
+/* Worker of call_for_symbol_thunks_and_aliases, it is supposed to be called on
+ the one caller of some other node. Set the caller's corresponding flag. */
+
+static bool
+set_single_call_flag (cgraph_node *node, void *)
+{
+ cgraph_edge *cs = node->callers;
+ /* Local thunks can be handled transparently, skip them. */
+ while (cs && cs->caller->thunk.thunk_p && cs->caller->local.local)
+ cs = cs->next_caller;
+ if (cs)
+ {
+ IPA_NODE_REF (cs->caller)->node_calling_single_call = true;
+ return true;
+ }
+ return false;
+}
+
/* Initialize ipcp_lattices. */
static void
int i;
gcc_checking_assert (node->has_gimple_body_p ());
- if (!cgraph_local_p (node))
+ if (cgraph_local_p (node))
+ {
+ int caller_count = 0;
+ node->call_for_symbol_thunks_and_aliases (count_callers, &caller_count,
+ true);
+ gcc_checking_assert (caller_count > 0);
+ if (caller_count == 1)
+ node->call_for_symbol_thunks_and_aliases (set_single_call_flag,
+ NULL, true);
+ }
+ else
{
/* When cloning is allowed, we can assume that externally visible
functions are not called. We will compensate this by cloning
plats->itself.set_to_bottom ();
plats->ctxlat.set_to_bottom ();
set_agg_lats_to_bottom (plats);
- set_alignment_to_bottom (plats);
+ plats->alignment.set_to_bottom ();
}
else
set_all_contains_variable (plats);
{
tree restype, res;
- gcc_checking_assert (is_gimple_ip_invariant (input));
if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
return input;
+ if (!is_gimple_ip_invariant (input))
+ return NULL_TREE;
if (TREE_CODE_CLASS (ipa_get_jf_pass_through_operation (jfunc))
== tcc_comparison)
{
tree t = TREE_OPERAND (input, 0);
t = build_ref_for_offset (EXPR_LOCATION (t), t,
- ipa_get_jf_ancestor_offset (jfunc),
+ ipa_get_jf_ancestor_offset (jfunc), false,
ptr_type_node, NULL, false);
return build_fold_addr_expr (t);
}
{
ipcp_value_source<valtype> *src;
- src = new (pool_alloc (ipcp_sources_pool)) ipcp_value_source<valtype>;
+ src = new (ipcp_sources_pool.allocate ()) ipcp_value_source<valtype>;
src->offset = offset;
src->cs = cs;
src->val = src_val;
{
ipcp_value<tree> *val;
- val = new (pool_alloc (ipcp_cst_values_pool)) ipcp_value<tree>;
+ val = ipcp_cst_values_pool.allocate ();
memset (val, 0, sizeof (*val));
val->value = source;
return val;
{
ipcp_value<ipa_polymorphic_call_context> *val;
- val = new (pool_alloc (ipcp_poly_ctx_values_pool))
- ipcp_value<ipa_polymorphic_call_context>;
+ // TODO
+ val = ipcp_poly_ctx_values_pool.allocate ();
memset (val, 0, sizeof (*val));
val->value = source;
return val;
if (values_count == PARAM_VALUE (PARAM_IPA_CP_VALUE_LIST_SIZE))
{
/* We can only free sources, not the values themselves, because sources
- of other values in this this SCC might point to them. */
+ of other values in this SCC might point to them. */
for (val = values; val; val = val->next)
{
while (val->sources)
{
ipcp_value_source<valtype> *src = val->sources;
val->sources = src->next;
- pool_free (ipcp_sources_pool, src);
+ ipcp_sources_pool.remove ((ipcp_value_source<tree>*)src);
}
}
edge CS and update DEST_LAT accordingly. */
static bool
-propagate_alignment_accross_jump_function (struct cgraph_edge *cs,
- struct ipa_jump_func *jfunc,
- struct ipcp_param_lattices *dest_lat)
+propagate_alignment_accross_jump_function (cgraph_edge *cs,
+ ipa_jump_func *jfunc,
+ ipcp_alignment_lattice *dest_lat)
{
- if (alignment_bottom_p (dest_lat))
+ if (dest_lat->bottom_p ())
return false;
- ipa_alignment cur;
- cur.known = false;
- if (jfunc->alignment.known)
- cur = jfunc->alignment;
- else if (jfunc->type == IPA_JF_PASS_THROUGH
+ if (jfunc->type == IPA_JF_PASS_THROUGH
|| jfunc->type == IPA_JF_ANCESTOR)
{
struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
- struct ipcp_param_lattices *src_lats;
HOST_WIDE_INT offset = 0;
int src_idx;
if (op != NOP_EXPR)
{
if (op != POINTER_PLUS_EXPR
- && op != PLUS_EXPR
- && op != MINUS_EXPR)
- goto prop_fail;
+ && op != PLUS_EXPR)
+ return dest_lat->set_to_bottom ();
tree operand = ipa_get_jf_pass_through_operand (jfunc);
if (!tree_fits_shwi_p (operand))
- goto prop_fail;
+ return dest_lat->set_to_bottom ();
offset = tree_to_shwi (operand);
}
src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
else
{
src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
- offset = ipa_get_jf_ancestor_offset (jfunc);
+ offset = ipa_get_jf_ancestor_offset (jfunc) / BITS_PER_UNIT;
}
+ struct ipcp_param_lattices *src_lats;
src_lats = ipa_get_parm_lattices (caller_info, src_idx);
- if (!src_lats->alignment.known
- || alignment_bottom_p (src_lats))
- goto prop_fail;
-
- cur = src_lats->alignment;
- cur.misalign = (cur.misalign + offset) % cur.align;
+ return dest_lat->meet_with (src_lats->alignment, offset);
}
-
- if (cur.known)
+ else
{
- if (!dest_lat->alignment.known)
- {
- dest_lat->alignment = cur;
- return true;
- }
- else if (dest_lat->alignment.align == cur.align
- && dest_lat->alignment.misalign == cur.misalign)
- return false;
+ if (jfunc->alignment.known)
+ return dest_lat->meet_with (jfunc->alignment.align,
+ jfunc->alignment.misalign);
+ else
+ return dest_lat->set_to_bottom ();
}
-
- prop_fail:
- set_alignment_to_bottom (dest_lat);
- return true;
}
/* If DEST_PLATS already has aggregate items, check that aggs_by_ref matches
if (dest_plats->aggs_count == PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS))
return false;
dest_plats->aggs_count++;
- new_al = (struct ipcp_agg_lattice *) pool_alloc (ipcp_agg_lattice_pool);
+ new_al = ipcp_agg_lattice_pool.allocate ();
memset (new_al, 0, sizeof (*new_al));
new_al->offset = offset;
return ret;
}
+/* Return true if on the way cfrom CS->caller to the final (non-alias and
+ non-thunk) destination, the call passes through a thunk. */
+
+static bool
+call_passes_through_thunk_p (cgraph_edge *cs)
+{
+ cgraph_node *alias_or_thunk = cs->callee;
+ while (alias_or_thunk->alias)
+ alias_or_thunk = alias_or_thunk->get_alias_target ();
+ return alias_or_thunk->thunk.thunk_p;
+}
+
/* Propagate constants from the caller to the callee of CS. INFO describes the
caller. */
{
struct ipa_node_params *callee_info;
enum availability availability;
- struct cgraph_node *callee, *alias_or_thunk;
+ cgraph_node *callee;
struct ipa_edge_args *args;
bool ret = false;
int i, args_count, parms_count;
/* 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
of aliases first. */
- alias_or_thunk = cs->callee;
- while (alias_or_thunk->alias)
- alias_or_thunk = alias_or_thunk->get_alias_target ();
- if (alias_or_thunk->thunk.thunk_p)
+ if (call_passes_through_thunk_p (cs))
{
ret |= set_all_contains_variable (ipa_get_parm_lattices (callee_info,
0));
ret |= propagate_context_accross_jump_function (cs, jump_func, i,
&dest_plats->ctxlat);
ret |= propagate_alignment_accross_jump_function (cs, jump_func,
- dest_plats);
+ &dest_plats->alignment);
ret |= propagate_aggs_accross_jump_function (cs, jump_func,
dest_plats);
}
if (ie->indirect_info->agg_contents)
{
- if (agg_reps)
+ t = NULL;
+ if (agg_reps && ie->indirect_info->guaranteed_unmodified)
{
- t = NULL;
while (agg_reps)
{
if (agg_reps->index == param_index
agg_reps = agg_reps->next;
}
}
- else if (known_aggs.length () > (unsigned int) param_index)
+ if (!t)
{
struct ipa_agg_jump_function *agg;
- agg = known_aggs[param_index];
- t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
- ie->indirect_info->by_ref);
+ if (known_aggs.length () > (unsigned int) param_index)
+ agg = known_aggs[param_index];
+ else
+ agg = NULL;
+ bool from_global_constant;
+ t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
+ ie->indirect_info->offset,
+ ie->indirect_info->by_ref,
+ &from_global_constant);
+ if (!from_global_constant
+ && !ie->indirect_info->guaranteed_unmodified)
+ t = NULL_TREE;
}
- else
- t = NULL;
}
else
t = known_csts[param_index];
{
struct ipa_agg_jump_function *agg;
agg = known_aggs[param_index];
- t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
+ t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
+ ie->indirect_info->offset,
true);
}
unsigned HOST_WIDE_INT offset;
if (vtable_pointer_value_to_vtable (t, &vtable, &offset))
{
+ bool can_refer;
target = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token,
- vtable, offset);
- if (target)
+ vtable, offset, &can_refer);
+ if (can_refer)
{
- if ((TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE
- && DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE)
+ if (!target
+ || (TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE
+ && DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE)
|| !possible_polymorphic_call_target_p
(ie, cgraph_node::get (target)))
- target = ipa_impossible_devirt_target (ie, target);
+ {
+ /* Do not speculate builtin_unreachable, it is stupid! */
+ if (ie->indirect_info->vptr_changed)
+ return NULL;
+ target = ipa_impossible_devirt_target (ie, target);
+ }
*speculative = ie->indirect_info->vptr_changed;
if (!*speculative)
return target;
if (target && !possible_polymorphic_call_target_p (ie,
cgraph_node::get (target)))
- target = ipa_impossible_devirt_target (ie, target);
+ {
+ if (*speculative)
+ return NULL;
+ target = ipa_impossible_devirt_target (ie, target);
+ }
return target;
}
return result;
}
+/* If there is a reason to penalize the function described by INFO in the
+ cloning goodness evaluation, do so. */
+
+static inline int64_t
+incorporate_penalties (ipa_node_params *info, int64_t evaluation)
+{
+ if (info->node_within_scc)
+ evaluation = (evaluation
+ * (100 - PARAM_VALUE (PARAM_IPA_CP_RECURSION_PENALTY))) / 100;
+
+ if (info->node_calling_single_call)
+ evaluation = (evaluation
+ * (100 - PARAM_VALUE (PARAM_IPA_CP_SINGLE_CALL_PENALTY)))
+ / 100;
+
+ return evaluation;
+}
+
/* Return true if cloning NODE is a good idea, given the estimated TIME_BENEFIT
and SIZE_COST and with the sum of frequencies of incoming edges to the
potential new clone in FREQUENCIES. */
{
if (time_benefit == 0
|| !opt_for_fn (node->decl, flag_ipa_cp_clone)
- || !optimize_function_for_speed_p (DECL_STRUCT_FUNCTION (node->decl)))
+ || node->optimize_for_size_p ())
return false;
gcc_assert (size_cost > 0);
+ struct ipa_node_params *info = IPA_NODE_REF (node);
if (max_count)
{
int factor = (count_sum * 1000) / max_count;
int64_t evaluation = (((int64_t) time_benefit * factor)
/ size_cost);
+ evaluation = incorporate_penalties (info, evaluation);
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: " "%"PRId64
+ "%s%s) -> evaluation: " "%" PRId64
", threshold: %i\n",
time_benefit, size_cost, (HOST_WIDE_INT) count_sum,
+ info->node_within_scc ? ", scc" : "",
+ info->node_calling_single_call ? ", single_call" : "",
evaluation, PARAM_VALUE (PARAM_IPA_CP_EVAL_THRESHOLD));
return evaluation >= PARAM_VALUE (PARAM_IPA_CP_EVAL_THRESHOLD);
{
int64_t evaluation = (((int64_t) time_benefit * freq_sum)
/ size_cost);
+ evaluation = incorporate_penalties (info, evaluation);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " good_cloning_opportunity_p (time: %i, "
- "size: %i, freq_sum: %i) -> evaluation: "
- "%"PRId64 ", threshold: %i\n",
- time_benefit, size_cost, freq_sum, evaluation,
- PARAM_VALUE (PARAM_IPA_CP_EVAL_THRESHOLD));
+ "size: %i, freq_sum: %i%s%s) -> evaluation: "
+ "%" PRId64 ", threshold: %i\n",
+ time_benefit, size_cost, freq_sum,
+ info->node_within_scc ? ", scc" : "",
+ info->node_calling_single_call ? ", single_call" : "",
+ evaluation, PARAM_VALUE (PARAM_IPA_CP_EVAL_THRESHOLD));
return evaluation >= PARAM_VALUE (PARAM_IPA_CP_EVAL_THRESHOLD);
}
*removable_params_cost
+= ipa_get_param_move_cost (info, i);
+ if (!ipa_is_param_used (info, i))
+ continue;
+
ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
+ /* Do not account known context as reason for cloning. We can see
+ if it permits devirtualization. */
if (ctxlat->is_single_const ())
- {
- (*known_contexts)[i] = ctxlat->values->value;
- ret = true;
- }
+ (*known_contexts)[i] = ctxlat->values->value;
if (known_aggs)
{
&known_contexts, &known_aggs,
&removable_params_cost);
known_aggs_ptrs = agg_jmp_p_vec_for_t_vec (known_aggs);
- if (always_const)
+ int devirt_bonus = devirtualization_time_bonus (node, known_csts,
+ known_contexts, known_aggs_ptrs);
+ if (always_const || devirt_bonus
+ || (removable_params_cost && node->local.can_change_signature))
{
struct caller_statistics stats;
inline_hints hints;
false);
estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
known_aggs_ptrs, &size, &time, &hints);
- time -= devirtualization_time_bonus (node, known_csts, known_contexts,
- known_aggs_ptrs);
+ time -= devirt_bonus;
time -= hint_time_bonus (hints);
time -= removable_params_cost;
size -= stats.n_calls * removable_params_cost;
fprintf (dump_file, " - context independent values, size: %i, "
"time_benefit: %i\n", size, base_time - time);
- if (size <= 0
- || node->will_be_removed_from_program_if_no_direct_calls_p ())
+ if (size <= 0 || node->local.local)
{
info->do_clone_for_all_contexts = true;
base_time = time;
"max_new_size would be reached with %li.\n",
size + overall_size);
}
+ else if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Not cloning for all contexts because "
+ "!good_cloning_opportunity_p.\n");
+
}
for (i = 0; i < count ; i++)
struct cgraph_edge *cs;
for (cs = v->callees; cs; cs = cs->next_callee)
- if (ipa_edge_within_scc (cs)
- && propagate_constants_accross_call (cs))
- push_node_to_stack (topo, cs->callee);
+ if (ipa_edge_within_scc (cs))
+ {
+ IPA_NODE_REF (v)->node_within_scc = true;
+ if (propagate_constants_accross_call (cs))
+ push_node_to_stack (topo, cs->callee->function_symbol ());
+ }
v = pop_node_from_stack (topo);
}
{
struct ipa_node_params *info = IPA_NODE_REF (node);
- determine_versionability (node);
+ determine_versionability (node, info);
if (node->has_gimple_body_p ())
{
info->lattices = XCNEWVEC (struct ipcp_param_lattices,
overall_size, max_new_size);
propagate_constants_topo (topo);
-#ifdef ENABLE_CHECKING
- ipcp_verify_propagated_values ();
-#endif
+ if (flag_checking)
+ ipcp_verify_propagated_values ();
topo->constants.propagate_effects ();
topo->contexts.propagate_effects ();
struct ipa_jump_func *jump_func;
tree t;
- if (i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs)))
+ if (i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs))
+ || (i == 0
+ && call_passes_through_thunk_p (cs))
+ || (!cs->callee->instrumentation_clone
+ && cs->callee->function_symbol ()->instrumentation_clone))
{
newval = NULL_TREE;
break;
/* Given an original NODE and a VAL for which we have already created a
specialized clone, look whether there are incoming edges that still lead
into the old node but now also bring the requested value and also conform to
- all other criteria such that they can be redirected the the special node.
+ all other criteria such that they can be redirected the special node.
This function can therefore redirect the final edge in a SCC. */
template <typename valtype>
{
struct cgraph_node *v;
for (v = node; v ; v = ((struct ipa_dfs_info *) v->aux)->next_cycle)
- if (v->will_be_removed_from_program_if_no_direct_calls_p ()
+ if (v->local.local
&& !v->call_for_symbol_thunks_and_aliases
(has_undead_caller_from_outside_scc_p, NULL, true))
IPA_NODE_REF (v)->node_dead = 1;
bool dumped_sth = false;
bool found_useful_result = false;
+ if (!opt_for_fn (node->decl, flag_ipa_cp_alignment))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Not considering %s for alignment discovery "
+ "and propagate; -fipa-cp-alignment: disabled.\n",
+ node->name ());
+ continue;
+ }
+
if (info->ipcp_orig_node)
info = IPA_NODE_REF (info->ipcp_orig_node);
for (unsigned i = 0; i < count ; i++)
{
ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
- if (plats->alignment.known
- && plats->alignment.align > 0)
+ if (!plats->alignment.bottom_p ()
+ && !plats->alignment.top_p ())
{
+ gcc_checking_assert (plats->alignment.align > 0);
found_useful_result = true;
break;
}
if (!found_useful_result)
continue;
- ipcp_grow_transformations_if_necessary ();
+ ipcp_grow_transformations_if_necessary ();
ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
vec_safe_reserve_exact (ts->alignments, count);
for (unsigned i = 0; i < count ; i++)
{
ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+ ipa_alignment al;
- if (plats->alignment.align == 0)
- plats->alignment.known = false;
+ if (!plats->alignment.bottom_p ()
+ && !plats->alignment.top_p ())
+ {
+ al.known = true;
+ al.align = plats->alignment.align;
+ al.misalign = plats->alignment.misalign;
+ }
+ else
+ al.known = false;
- ts->alignments->quick_push (plats->alignment);
- if (!dump_file || !plats->alignment.known)
+ ts->alignments->quick_push (al);
+ if (!dump_file || !al.known)
continue;
if (!dumped_sth)
{
dumped_sth = true;
}
fprintf (dump_file, " param %i: align: %u, misalign: %u\n",
- i, plats->alignment.align, plats->alignment.misalign);
+ i, al.align, al.misalign);
}
}
}
edge_removal_hook_holder =
symtab->add_edge_removal_hook (&ipcp_edge_removal_hook, NULL);
- ipcp_cst_values_pool = create_alloc_pool ("IPA-CP constant values",
- sizeof (ipcp_value<tree>), 32);
- ipcp_poly_ctx_values_pool = create_alloc_pool
- ("IPA-CP polymorphic contexts",
- sizeof (ipcp_value<ipa_polymorphic_call_context>), 32);
- ipcp_sources_pool = create_alloc_pool ("IPA-CP value sources",
- sizeof (ipcp_value_source<tree>), 64);
- ipcp_agg_lattice_pool = create_alloc_pool ("IPA_CP aggregate lattices",
- sizeof (struct ipcp_agg_lattice),
- 32);
if (dump_file)
{
fprintf (dump_file, "\nIPA structures before propagation:\n");
/* Free all IPCP structures. */
free_toporder_info (&topo);
next_edge_clone.release ();
+ prev_edge_clone.release ();
symtab->remove_edge_removal_hook (edge_removal_hook_holder);
symtab->remove_edge_duplication_hook (edge_duplication_hook_holder);
ipa_free_all_structures_after_ipa_cp ();
ipa_register_cgraph_hooks ();
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
- {
- node->local.versionable
- = tree_versionable_function_p (node->decl);
- ipa_analyze_node (node);
- }
+ ipa_analyze_node (node);
}
/* Write ipcp summary for nodes in SET. */