/* Interprocedural constant propagation
- Copyright (C) 2005-2015 Free Software Foundation, Inc.
+ Copyright (C) 2005-2017 Free Software Foundation, Inc.
Contributed by Razya Ladelsky <RAZYA@il.ibm.com> and Martin Jambor
<mjambor@suse.cz>
values:
Pass through - the caller's formal parameter is passed as an actual
- argument, plus an operation on it can be performed.
+ argument, plus an operation on it can be performed.
Constant - a constant is passed as an actual argument.
Unknown - neither of the above.
#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 "tree-vrp.h"
+#include "ipa-prop.h"
#include "tree-pretty-print.h"
#include "tree-inline.h"
#include "params.h"
-#include "ipa-inline.h"
+#include "ipa-fnsummary.h"
#include "ipa-utils.h"
+#include "tree-ssa-ccp.h"
+#include "stringpool.h"
+#include "attribs.h"
template <typename valtype> class ipcp_value;
/* Time benefit and size cost that specializing the function for this value
can bring about in it's callees (transitively). */
int prop_time_benefit, prop_size_cost;
+
+ ipcp_value_base ()
+ : local_time_benefit (0), local_size_cost (0),
+ prop_time_benefit (0), prop_size_cost (0) {}
};
/* Describes one particular value stored in struct ipcp_lattice. */
/* True if this valye is currently on the topo-sort stack. */
bool on_stack;
+ ipcp_value()
+ : sources (0), next (0), scc_next (0), topo_next (0),
+ spec_node (0), dfs (0), low_link (0), on_stack (false) {}
+
void add_source (cgraph_edge *cs, ipcp_value *src_val, int src_idx,
HOST_WIDE_INT offset);
};
/* Lattice describing potential values of a formal parameter of a function, or
- a part of an aggreagate. TOP is represented by a lattice with zero values
+ a part of an aggregate. TOP is represented by a lattice with zero values
and with contains_variable and bottom flags cleared. BOTTOM is represented
by a lattice with the bottom flag set. In that case, values and
contains_variable flag should be disregarded. */
struct ipcp_agg_lattice *next;
};
+/* Lattice of known bits, only capable of holding one value.
+ Bitwise constant propagation propagates which bits of a
+ value are constant.
+ For eg:
+ int f(int x)
+ {
+ return some_op (x);
+ }
+
+ int f1(int y)
+ {
+ if (cond)
+ return f (y & 0xff);
+ else
+ return f (y & 0xf);
+ }
+
+ In the above case, the param 'x' will always have all
+ the bits (except the bits in lsb) set to 0.
+ Hence the mask of 'x' would be 0xff. The mask
+ reflects that the bits in lsb are unknown.
+ The actual propagated value is given by m_value & ~m_mask. */
+
+class ipcp_bits_lattice
+{
+public:
+ bool bottom_p () { return m_lattice_val == IPA_BITS_VARYING; }
+ bool top_p () { return m_lattice_val == IPA_BITS_UNDEFINED; }
+ bool constant_p () { return m_lattice_val == IPA_BITS_CONSTANT; }
+ bool set_to_bottom ();
+ bool set_to_constant (widest_int, widest_int);
+
+ widest_int get_value () { return m_value; }
+ widest_int get_mask () { return m_mask; }
+
+ bool meet_with (ipcp_bits_lattice& other, unsigned, signop,
+ enum tree_code, tree);
+
+ bool meet_with (widest_int, widest_int, unsigned);
+
+ void print (FILE *);
+
+private:
+ enum { IPA_BITS_UNDEFINED, IPA_BITS_CONSTANT, IPA_BITS_VARYING } m_lattice_val;
+
+ /* Similar to ccp_lattice_t, mask represents which bits of value are constant.
+ If a bit in mask is set to 0, then the corresponding bit in
+ value is known to be constant. */
+ widest_int m_value, m_mask;
+
+ bool meet_with_1 (widest_int, widest_int, unsigned);
+ void get_value_and_mask (tree, widest_int *, widest_int *);
+};
+
+/* Lattice of value ranges. */
+
+class ipcp_vr_lattice
+{
+public:
+ value_range m_vr;
+
+ inline bool bottom_p () const;
+ inline bool top_p () const;
+ inline bool set_to_bottom ();
+ bool meet_with (const value_range *p_vr);
+ bool meet_with (const ipcp_vr_lattice &other);
+ void init () { m_vr.type = VR_UNDEFINED; }
+ void print (FILE * f);
+
+private:
+ bool meet_with_1 (const value_range *other_vr);
+};
+
/* 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 bits. */
+ ipcp_bits_lattice bits_lattice;
+ /* Lattice describing value range. */
+ ipcp_vr_lattice m_value_range;
/* 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. */
-static gcov_type max_count;
+static profile_count max_count;
/* Original overall size of the program. */
return &plats->ctxlat;
}
+/* Return the lattice corresponding to the value range of the Ith formal
+ parameter of the function described by INFO. */
+
+static inline ipcp_vr_lattice *
+ipa_get_vr_lat (struct ipa_node_params *info, int i)
+{
+ struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+ return &plats->m_value_range;
+}
+
/* Return whether LAT is a lattice with a single constant and without an
undefined value. */
print_ipcp_constant_value (FILE * f, tree v)
{
if (TREE_CODE (v) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (v, 0)) == CONST_DECL)
+ && TREE_CODE (TREE_OPERAND (v, 0)) == CONST_DECL)
{
fprintf (f, "& ");
- print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (v, 0)), 0);
+ print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (v, 0)));
}
else
- print_generic_expr (f, v, 0);
+ print_generic_expr (f, v);
}
/* Print V which is extracted from a value in a lattice to F. */
fprintf (f, "\n");
}
+void
+ipcp_bits_lattice::print (FILE *f)
+{
+ if (top_p ())
+ fprintf (f, " Bits unknown (TOP)\n");
+ else if (bottom_p ())
+ fprintf (f, " Bits unusable (BOTTOM)\n");
+ else
+ {
+ fprintf (f, " Bits: value = "); print_hex (get_value (), f);
+ fprintf (f, ", mask = "); print_hex (get_mask (), f);
+ fprintf (f, "\n");
+ }
+}
+
+/* Print value range lattice to F. */
+
+void
+ipcp_vr_lattice::print (FILE * f)
+{
+ dump_value_range (f, &m_vr);
+}
+
/* Print all ipcp_lattices of all functions to F. */
static void
struct ipa_node_params *info;
info = IPA_NODE_REF (node);
- fprintf (f, " Node: %s/%i:\n", node->name (),
- node->order);
+ fprintf (f, " Node: %s:\n", node->dump_name ());
count = ipa_get_param_count (info);
for (i = 0; i < count; i++)
{
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->bits_lattice.print (f);
+ fprintf (f, " ");
+ plats->m_value_range.print (f);
+ fprintf (f, "\n");
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;
coexist, but that may not be worth the effort. */
reason = "function has SIMD clones";
}
+ else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (node->decl)))
+ {
+ /* Ideally we should clone the target clones themselves and create
+ copies of them, so IPA-cp and target clones can happily
+ coexist, but that may not be worth the effort. */
+ reason = "function target_clones attribute";
+ }
/* Don't clone decls local to a comdat group; it breaks and for C++
decloned constructors, inlining is always better anyway. */
else if (node->comdat_local_p ())
reason = "comdat-local function";
+ else if (node->calls_comdat_local)
+ {
+ /* TODO: call is versionable if we make sure that all
+ callers are inside of a comdat group. */
+ reason = "calls comdat-local function";
+ }
if (reason && dump_file && !node->alias && !node->thunk.thunk_p)
- fprintf (dump_file, "Function %s/%i is not versionable, reason: %s.\n",
- node->name (), node->order, reason);
+ fprintf (dump_file, "Function %s is not versionable, reason: %s.\n",
+ node->dump_name (), 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 caller_statistics
{
- gcov_type count_sum;
+ profile_count count_sum;
int n_calls, n_hot_calls, freq_sum;
};
static inline void
init_caller_stats (struct caller_statistics *stats)
{
- stats->count_sum = 0;
+ stats->count_sum = profile_count::zero ();
stats->n_calls = 0;
stats->n_hot_calls = 0;
stats->freq_sum = 0;
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;
+ if (cs->count.initialized_p ())
+ stats->count_sum += cs->count;
stats->freq_sum += cs->frequency;
stats->n_calls++;
if (cs->maybe_hot_p ())
if (!opt_for_fn (node->decl, flag_ipa_cp_clone))
{
if (dump_file)
- fprintf (dump_file, "Not considering %s for cloning; "
+ fprintf (dump_file, "Not considering %s for cloning; "
"-fipa-cp-clone disabled.\n",
- node->name ());
+ node->name ());
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; "
+ fprintf (dump_file, "Not considering %s for cloning; "
"optimizing it for size.\n",
- node->name ());
+ node->name ());
return false;
}
init_caller_stats (&stats);
node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats, false);
- if (inline_summaries->get (node)->self_size < stats.n_calls)
+ if (ipa_fn_summaries->get (node)->self_size < stats.n_calls)
{
if (dump_file)
- fprintf (dump_file, "Considering %s for cloning; code might shrink.\n",
- node->name ());
+ fprintf (dump_file, "Considering %s for cloning; code might shrink.\n",
+ node->name ());
return true;
}
/* When profile is available and function is hot, propagate into it even if
calls seems cold; constant propagation can improve function's speed
significantly. */
- if (max_count)
+ if (max_count > profile_count::zero ())
{
- if (stats.count_sum > node->count * 90 / 100)
+ if (stats.count_sum > node->count.apply_scale (90, 100))
{
if (dump_file)
fprintf (dump_file, "Considering %s for cloning; "
"usually called directly.\n",
node->name ());
return true;
- }
+ }
}
if (!stats.n_hot_calls)
{
return ret;
}
-/* Return true if alignment information in PLATS is known to be unusable. */
+bool
+ipcp_vr_lattice::meet_with (const ipcp_vr_lattice &other)
+{
+ return meet_with_1 (&other.m_vr);
+}
+
+/* Meet the current value of the lattice with value ranfge described by VR
+ lattice. */
-static inline bool
-alignment_bottom_p (ipcp_param_lattices *plats)
+bool
+ipcp_vr_lattice::meet_with (const value_range *p_vr)
{
- return plats->alignment.known && (plats->alignment.align == 0);
+ return meet_with_1 (p_vr);
}
-/* Set alignment information in PLATS to unusable. Return true if it
- previously was usable or unknown. */
+/* Meet the current value of the lattice with value ranfge described by
+ OTHER_VR lattice. */
-static inline bool
-set_alignment_to_bottom (ipcp_param_lattices *plats)
+bool
+ipcp_vr_lattice::meet_with_1 (const value_range *other_vr)
+{
+ tree min = m_vr.min, max = m_vr.max;
+ value_range_type type = m_vr.type;
+
+ if (bottom_p ())
+ return false;
+
+ if (other_vr->type == VR_VARYING)
+ return set_to_bottom ();
+
+ vrp_meet (&m_vr, other_vr);
+ if (type != m_vr.type
+ || min != m_vr.min
+ || max != m_vr.max)
+ return true;
+ else
+ return false;
+}
+
+/* Return true if value range information in the lattice is yet unknown. */
+
+bool
+ipcp_vr_lattice::top_p () const
+{
+ return m_vr.type == VR_UNDEFINED;
+}
+
+/* Return true if value range information in the lattice is known to be
+ unusable. */
+
+bool
+ipcp_vr_lattice::bottom_p () const
+{
+ return m_vr.type == VR_VARYING;
+}
+
+/* Set value range information in the lattice to bottom. Return true if it
+ previously was in a different state. */
+
+bool
+ipcp_vr_lattice::set_to_bottom ()
+{
+ if (m_vr.type == VR_VARYING)
+ return false;
+ m_vr.type = VR_VARYING;
+ return true;
+}
+
+/* Set lattice value to bottom, if it already isn't the case. */
+
+bool
+ipcp_bits_lattice::set_to_bottom ()
{
- if (alignment_bottom_p (plats))
+ if (bottom_p ())
return false;
- plats->alignment.known = true;
- plats->alignment.align = 0;
+ m_lattice_val = IPA_BITS_VARYING;
+ m_value = 0;
+ m_mask = -1;
+ return true;
+}
+
+/* Set to constant if it isn't already. Only meant to be called
+ when switching state from TOP. */
+
+bool
+ipcp_bits_lattice::set_to_constant (widest_int value, widest_int mask)
+{
+ gcc_assert (top_p ());
+ m_lattice_val = IPA_BITS_CONSTANT;
+ m_value = value;
+ m_mask = mask;
return true;
}
+/* Convert operand to value, mask form. */
+
+void
+ipcp_bits_lattice::get_value_and_mask (tree operand, widest_int *valuep, widest_int *maskp)
+{
+ wide_int get_nonzero_bits (const_tree);
+
+ if (TREE_CODE (operand) == INTEGER_CST)
+ {
+ *valuep = wi::to_widest (operand);
+ *maskp = 0;
+ }
+ else
+ {
+ *valuep = 0;
+ *maskp = -1;
+ }
+}
+
+/* Meet operation, similar to ccp_lattice_meet, we xor values
+ if this->value, value have different values at same bit positions, we want
+ to drop that bit to varying. Return true if mask is changed.
+ This function assumes that the lattice value is in CONSTANT state */
+
+bool
+ipcp_bits_lattice::meet_with_1 (widest_int value, widest_int mask,
+ unsigned precision)
+{
+ gcc_assert (constant_p ());
+
+ widest_int old_mask = m_mask;
+ m_mask = (m_mask | mask) | (m_value ^ value);
+
+ if (wi::sext (m_mask, precision) == -1)
+ return set_to_bottom ();
+
+ return m_mask != old_mask;
+}
+
+/* Meet the bits lattice with operand
+ described by <value, mask, sgn, precision. */
+
+bool
+ipcp_bits_lattice::meet_with (widest_int value, widest_int mask,
+ unsigned precision)
+{
+ if (bottom_p ())
+ return false;
+
+ if (top_p ())
+ {
+ if (wi::sext (mask, precision) == -1)
+ return set_to_bottom ();
+ return set_to_constant (value, mask);
+ }
+
+ return meet_with_1 (value, mask, precision);
+}
+
+/* Meet bits lattice with the result of bit_value_binop (other, operand)
+ if code is binary operation or bit_value_unop (other) if code is unary op.
+ In the case when code is nop_expr, no adjustment is required. */
+
+bool
+ipcp_bits_lattice::meet_with (ipcp_bits_lattice& other, unsigned precision,
+ signop sgn, enum tree_code code, tree operand)
+{
+ if (other.bottom_p ())
+ return set_to_bottom ();
+
+ if (bottom_p () || other.top_p ())
+ return false;
+
+ widest_int adjusted_value, adjusted_mask;
+
+ if (TREE_CODE_CLASS (code) == tcc_binary)
+ {
+ tree type = TREE_TYPE (operand);
+ gcc_assert (INTEGRAL_TYPE_P (type));
+ widest_int o_value, o_mask;
+ get_value_and_mask (operand, &o_value, &o_mask);
+
+ bit_value_binop (code, sgn, precision, &adjusted_value, &adjusted_mask,
+ sgn, precision, other.get_value (), other.get_mask (),
+ TYPE_SIGN (type), TYPE_PRECISION (type), o_value, o_mask);
+
+ if (wi::sext (adjusted_mask, precision) == -1)
+ return set_to_bottom ();
+ }
+
+ else if (TREE_CODE_CLASS (code) == tcc_unary)
+ {
+ bit_value_unop (code, sgn, precision, &adjusted_value,
+ &adjusted_mask, sgn, precision, other.get_value (),
+ other.get_mask ());
+
+ if (wi::sext (adjusted_mask, precision) == -1)
+ return set_to_bottom ();
+ }
+
+ else
+ return set_to_bottom ();
+
+ if (top_p ())
+ {
+ if (wi::sext (adjusted_mask, precision) == -1)
+ return set_to_bottom ();
+ return set_to_constant (adjusted_value, adjusted_mask);
+ }
+ else
+ return meet_with_1 (adjusted_value, adjusted_mask, precision);
+}
+
/* 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->bits_lattice.set_to_bottom ();
+ ret |= plats->m_value_range.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
disable = true;
}
+ for (i = 0; i < ipa_get_param_count (info); i++)
+ {
+ struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+ plats->m_value_range.init ();
+ }
+
if (disable || variable)
{
- for (i = 0; i < ipa_get_param_count (info) ; i++)
+ for (i = 0; i < ipa_get_param_count (info); i++)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
if (disable)
plats->itself.set_to_bottom ();
plats->ctxlat.set_to_bottom ();
set_agg_lats_to_bottom (plats);
- set_alignment_to_bottom (plats);
+ plats->bits_lattice.set_to_bottom ();
+ plats->m_value_range.set_to_bottom ();
}
else
set_all_contains_variable (plats);
}
if (dump_file && (dump_flags & TDF_DETAILS)
&& !node->alias && !node->thunk.thunk_p)
- fprintf (dump_file, "Marking all lattices of %s/%i as %s\n",
- node->name (), node->order,
- disable ? "BOTTOM" : "VARIABLE");
+ fprintf (dump_file, "Marking all lattices of %s as %s\n",
+ node->dump_name (), disable ? "BOTTOM" : "VARIABLE");
}
for (ie = node->indirect_calls; ie; ie = ie->next_callee)
if (ie->indirect_info->polymorphic
- && ie->indirect_info->param_index >= 0)
+ && ie->indirect_info->param_index >= 0)
{
gcc_checking_assert (ie->indirect_info->param_index >= 0);
ipa_get_parm_lattices (info,
{
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)
- restype = boolean_type_node;
+ == tcc_unary)
+ res = fold_unary (ipa_get_jf_pass_through_operation (jfunc),
+ TREE_TYPE (input), input);
else
- restype = TREE_TYPE (input);
- res = fold_binary (ipa_get_jf_pass_through_operation (jfunc), restype,
- input, ipa_get_jf_pass_through_operand (jfunc));
-
+ {
+ if (TREE_CODE_CLASS (ipa_get_jf_pass_through_operation (jfunc))
+ == tcc_comparison)
+ restype = boolean_type_node;
+ else
+ restype = TREE_TYPE (input);
+ res = fold_binary (ipa_get_jf_pass_through_operation (jfunc), restype,
+ input, ipa_get_jf_pass_through_operand (jfunc));
+ }
if (res && !is_gimple_ip_invariant (res))
return NULL_TREE;
{
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_lattice<tree> *lat;
- if (!info->lattices)
+ if (!info->lattices
+ || idx >= ipa_get_param_count (info))
return NULL_TREE;
lat = ipa_get_scalar_lat (info, idx);
if (!lat->is_single_const ())
}
else
{
- if (!info->lattices)
+ if (!info->lattices
+ || srcidx >= ipa_get_param_count (info))
return ctx;
ipcp_lattice<ipa_polymorphic_call_context> *lat;
lat = ipa_get_poly_ctx_lat (info, srcidx);
{
if (dump_file)
{
- symtab_node::dump_table (dump_file);
+ symtab->dump (dump_file);
fprintf (dump_file, "\nIPA lattices after constant "
"propagation, before gcc_unreachable:\n");
print_all_lattices (dump_file, true, false);
{
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>;
- memset (val, 0, sizeof (*val));
+ val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
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>;
- memset (val, 0, sizeof (*val));
+ // TODO
+ val = new (ipcp_poly_ctx_values_pool.allocate ())
+ ipcp_value<ipa_polymorphic_call_context>();
val->value = source;
return val;
}
if (ipa_edge_within_scc (cs))
{
ipcp_value_source<valtype> *s;
- for (s = val->sources; s ; s = s->next)
+ for (s = val->sources; s; s = s->next)
if (s->cs == cs)
break;
if (s)
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);
}
}
is the index of the source parameter. */
static bool
-propagate_vals_accross_pass_through (cgraph_edge *cs,
- ipa_jump_func *jfunc,
- ipcp_lattice<tree> *src_lat,
- ipcp_lattice<tree> *dest_lat,
- int src_idx)
+propagate_vals_across_pass_through (cgraph_edge *cs, ipa_jump_func *jfunc,
+ ipcp_lattice<tree> *src_lat,
+ ipcp_lattice<tree> *dest_lat, int src_idx)
{
ipcp_value<tree> *src_val;
bool ret = false;
is the index of the source parameter. */
static bool
-propagate_vals_accross_ancestor (struct cgraph_edge *cs,
- struct ipa_jump_func *jfunc,
- ipcp_lattice<tree> *src_lat,
- ipcp_lattice<tree> *dest_lat,
- int src_idx)
+propagate_vals_across_ancestor (struct cgraph_edge *cs,
+ struct ipa_jump_func *jfunc,
+ ipcp_lattice<tree> *src_lat,
+ ipcp_lattice<tree> *dest_lat, int src_idx)
{
ipcp_value<tree> *src_val;
bool ret = false;
edge CS and put the values into DEST_LAT. */
static bool
-propagate_scalar_accross_jump_function (struct cgraph_edge *cs,
- struct ipa_jump_func *jfunc,
- ipcp_lattice<tree> *dest_lat)
+propagate_scalar_across_jump_function (struct cgraph_edge *cs,
+ struct ipa_jump_func *jfunc,
+ ipcp_lattice<tree> *dest_lat)
{
if (dest_lat->bottom)
return false;
return dest_lat->set_contains_variable ();
if (jfunc->type == IPA_JF_PASS_THROUGH)
- ret = propagate_vals_accross_pass_through (cs, jfunc, src_lat,
- dest_lat, src_idx);
+ ret = propagate_vals_across_pass_through (cs, jfunc, src_lat,
+ dest_lat, src_idx);
else
- ret = propagate_vals_accross_ancestor (cs, jfunc, src_lat, dest_lat,
- src_idx);
+ ret = propagate_vals_across_ancestor (cs, jfunc, src_lat, dest_lat,
+ src_idx);
if (src_lat->contains_variable)
ret |= dest_lat->set_contains_variable ();
edge CS and describes argument IDX and put the values into DEST_LAT. */
static bool
-propagate_context_accross_jump_function (cgraph_edge *cs,
+propagate_context_across_jump_function (cgraph_edge *cs,
ipa_jump_func *jfunc, int idx,
ipcp_lattice<ipa_polymorphic_call_context> *dest_lat)
{
return ret;
}
-/* Propagate alignments across jump function JFUNC that is associated with
- edge CS and update DEST_LAT accordingly. */
+/* Propagate bits across jfunc that is associated with
+ edge cs and update dest_lattice accordingly. */
-static bool
-propagate_alignment_accross_jump_function (struct cgraph_edge *cs,
- struct ipa_jump_func *jfunc,
- struct ipcp_param_lattices *dest_lat)
+bool
+propagate_bits_across_jump_function (cgraph_edge *cs, int idx,
+ ipa_jump_func *jfunc,
+ ipcp_bits_lattice *dest_lattice)
{
- if (alignment_bottom_p (dest_lat))
+ if (dest_lattice->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
- || jfunc->type == IPA_JF_ANCESTOR)
+ enum availability availability;
+ cgraph_node *callee = cs->callee->function_symbol (&availability);
+ struct ipa_node_params *callee_info = IPA_NODE_REF (callee);
+ tree parm_type = ipa_get_type (callee_info, idx);
+
+ /* For K&R C programs, ipa_get_type() could return NULL_TREE.
+ Avoid the transform for these cases. */
+ if (!parm_type)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Setting dest_lattice to bottom, because"
+ " param %i type is NULL for %s\n", idx,
+ cs->callee->name ());
+
+ return dest_lattice->set_to_bottom ();
+ }
+
+ unsigned precision = TYPE_PRECISION (parm_type);
+ signop sgn = TYPE_SIGN (parm_type);
+
+ 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;
+ tree operand = NULL_TREE;
+ enum tree_code code;
+ unsigned src_idx;
if (jfunc->type == IPA_JF_PASS_THROUGH)
{
- enum tree_code op = ipa_get_jf_pass_through_operation (jfunc);
- if (op != NOP_EXPR)
- {
- if (op != POINTER_PLUS_EXPR
- && op != PLUS_EXPR
- && op != MINUS_EXPR)
- goto prop_fail;
- tree operand = ipa_get_jf_pass_through_operand (jfunc);
- if (!tree_fits_shwi_p (operand))
- goto prop_fail;
- offset = tree_to_shwi (operand);
- }
+ code = ipa_get_jf_pass_through_operation (jfunc);
src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
+ if (code != NOP_EXPR)
+ operand = ipa_get_jf_pass_through_operand (jfunc);
}
else
{
+ code = POINTER_PLUS_EXPR;
src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
- offset = ipa_get_jf_ancestor_offset (jfunc);
+ unsigned HOST_WIDE_INT offset = ipa_get_jf_ancestor_offset (jfunc) / BITS_PER_UNIT;
+ operand = build_int_cstu (size_type_node, offset);
}
- src_lats = ipa_get_parm_lattices (caller_info, src_idx);
- if (!src_lats->alignment.known
- || alignment_bottom_p (src_lats))
- goto prop_fail;
+ struct ipcp_param_lattices *src_lats
+ = ipa_get_parm_lattices (caller_info, src_idx);
- cur = src_lats->alignment;
- cur.misalign = (cur.misalign + offset) % cur.align;
+ /* Try to propagate bits if src_lattice is bottom, but jfunc is known.
+ for eg consider:
+ int f(int x)
+ {
+ g (x & 0xff);
+ }
+ Assume lattice for x is bottom, however we can still propagate
+ result of x & 0xff == 0xff, which gets computed during ccp1 pass
+ and we store it in jump function during analysis stage. */
+
+ if (src_lats->bits_lattice.bottom_p ()
+ && jfunc->bits)
+ return dest_lattice->meet_with (jfunc->bits->value, jfunc->bits->mask,
+ precision);
+ else
+ return dest_lattice->meet_with (src_lats->bits_lattice, precision, sgn,
+ code, operand);
}
- if (cur.known)
+ else if (jfunc->type == IPA_JF_ANCESTOR)
+ return dest_lattice->set_to_bottom ();
+ else if (jfunc->bits)
+ return dest_lattice->meet_with (jfunc->bits->value, jfunc->bits->mask,
+ precision);
+ else
+ return dest_lattice->set_to_bottom ();
+}
+
+/* Emulate effects of unary OPERATION and/or conversion from SRC_TYPE to
+ DST_TYPE on value range in SRC_VR and store it to DST_VR. Return true if
+ the result is a range or an anti-range. */
+
+static bool
+ipa_vr_operation_and_type_effects (value_range *dst_vr, value_range *src_vr,
+ enum tree_code operation,
+ tree dst_type, tree src_type)
+{
+ memset (dst_vr, 0, sizeof (*dst_vr));
+ extract_range_from_unary_expr (dst_vr, operation, dst_type, src_vr, src_type);
+ if (dst_vr->type == VR_RANGE || dst_vr->type == VR_ANTI_RANGE)
+ return true;
+ else
+ return false;
+}
+
+/* Propagate value range across jump function JFUNC that is associated with
+ edge CS with param of callee of PARAM_TYPE and update DEST_PLATS
+ accordingly. */
+
+static bool
+propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc,
+ struct ipcp_param_lattices *dest_plats,
+ tree param_type)
+{
+ ipcp_vr_lattice *dest_lat = &dest_plats->m_value_range;
+
+ if (dest_lat->bottom_p ())
+ return false;
+
+ if (!param_type
+ || (!INTEGRAL_TYPE_P (param_type)
+ && !POINTER_TYPE_P (param_type)))
+ return dest_lat->set_to_bottom ();
+
+ if (jfunc->type == IPA_JF_PASS_THROUGH)
{
- if (!dest_lat->alignment.known)
+ enum tree_code operation = ipa_get_jf_pass_through_operation (jfunc);
+
+ if (TREE_CODE_CLASS (operation) == tcc_unary)
{
- dest_lat->alignment = cur;
- return true;
+ struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
+ int src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
+ tree operand_type = ipa_get_type (caller_info, src_idx);
+ struct ipcp_param_lattices *src_lats
+ = ipa_get_parm_lattices (caller_info, src_idx);
+
+ if (src_lats->m_value_range.bottom_p ())
+ return dest_lat->set_to_bottom ();
+ value_range vr;
+ if (ipa_vr_operation_and_type_effects (&vr,
+ &src_lats->m_value_range.m_vr,
+ operation, param_type,
+ operand_type))
+ return dest_lat->meet_with (&vr);
+ }
+ }
+ else if (jfunc->type == IPA_JF_CONST)
+ {
+ tree val = ipa_get_jf_constant (jfunc);
+ if (TREE_CODE (val) == INTEGER_CST)
+ {
+ val = fold_convert (param_type, val);
+ if (TREE_OVERFLOW_P (val))
+ val = drop_tree_overflow (val);
+
+ value_range tmpvr;
+ memset (&tmpvr, 0, sizeof (tmpvr));
+ tmpvr.type = VR_RANGE;
+ tmpvr.min = val;
+ tmpvr.max = val;
+ return dest_lat->meet_with (&tmpvr);
}
- else if (dest_lat->alignment.align == cur.align
- && dest_lat->alignment.misalign == cur.misalign)
- return false;
}
- prop_fail:
- set_alignment_to_bottom (dest_lat);
- return true;
+ value_range vr;
+ if (jfunc->m_vr
+ && ipa_vr_operation_and_type_effects (&vr, jfunc->m_vr, NOP_EXPR,
+ param_type,
+ TREE_TYPE (jfunc->m_vr->min)))
+ return dest_lat->meet_with (&vr);
+ else
+ return dest_lat->set_to_bottom ();
}
/* If DEST_PLATS already has aggregate items, check that aggs_by_ref matches
if (**aglat && (**aglat)->offset == offset)
{
if ((**aglat)->size != val_size
- || ((**aglat)->next
- && (**aglat)->next->offset < offset + val_size))
+ || ((**aglat)->next
+ && (**aglat)->next->offset < offset + val_size))
{
set_agg_lats_to_bottom (dest_plats);
return false;
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;
edge CS and put the values into DEST_LAT. */
static bool
-propagate_aggs_accross_jump_function (struct cgraph_edge *cs,
- struct ipa_jump_func *jfunc,
- struct ipcp_param_lattices *dest_plats)
+propagate_aggs_across_jump_function (struct cgraph_edge *cs,
+ struct ipa_jump_func *jfunc,
+ struct ipcp_param_lattices *dest_plats)
{
bool ret = false;
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. */
static bool
-propagate_constants_accross_call (struct cgraph_edge *cs)
+propagate_constants_across_call (struct cgraph_edge *cs)
{
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));
{
struct ipa_jump_func *jump_func = ipa_get_ith_jump_func (args, i);
struct ipcp_param_lattices *dest_plats;
+ tree param_type = ipa_get_type (callee_info, i);
dest_plats = ipa_get_parm_lattices (callee_info, i);
if (availability == AVAIL_INTERPOSABLE)
ret |= set_all_contains_variable (dest_plats);
else
{
- ret |= propagate_scalar_accross_jump_function (cs, jump_func,
- &dest_plats->itself);
- ret |= propagate_context_accross_jump_function (cs, jump_func, i,
- &dest_plats->ctxlat);
- ret |= propagate_alignment_accross_jump_function (cs, jump_func,
- dest_plats);
- ret |= propagate_aggs_accross_jump_function (cs, jump_func,
- dest_plats);
+ ret |= propagate_scalar_across_jump_function (cs, jump_func,
+ &dest_plats->itself);
+ ret |= propagate_context_across_jump_function (cs, jump_func, i,
+ &dest_plats->ctxlat);
+ ret
+ |= propagate_bits_across_jump_function (cs, i, jump_func,
+ &dest_plats->bits_lattice);
+ ret |= propagate_aggs_across_jump_function (cs, jump_func,
+ dest_plats);
+ if (opt_for_fn (callee->decl, flag_ipa_vrp))
+ ret |= propagate_vr_across_jump_function (cs, jump_func,
+ dest_plats, param_type);
+ else
+ ret |= dest_plats->m_value_range.set_to_bottom ();
}
}
for (; i < parms_count; i++)
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 (t
+ && !from_global_constant
+ && !ie->indirect_info->guaranteed_unmodified)
+ t = NULL_TREE;
}
- else
- t = NULL;
}
else
t = known_csts[param_index];
- if (t &&
- TREE_CODE (t) == ADDR_EXPR
+ if (t
+ && TREE_CODE (t) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL)
return TREE_OPERAND (t, 0);
else
if (!t && known_aggs.length () > (unsigned int) param_index
&& !ie->indirect_info->by_ref)
{
- struct ipa_agg_jump_function *agg;
- agg = known_aggs[param_index];
- t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
- true);
+ struct ipa_agg_jump_function *agg;
+ agg = known_aggs[param_index];
+ t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
+ ie->indirect_info->offset, true);
}
/* If we found the virtual table pointer, lookup the target. */
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);
- *speculative = ie->indirect_info->vptr_changed;
+ {
+ /* 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;
+ return target;
}
}
}
}
}
else if (t)
- context = ipa_polymorphic_call_context (t, ie->indirect_info->otr_type,
- anc_offset);
+ {
+ context = ipa_polymorphic_call_context (t, ie->indirect_info->otr_type,
+ anc_offset);
+ if (ie->indirect_info->vptr_changed)
+ context.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
+ ie->indirect_info->otr_type);
+ }
else
return NULL_TREE;
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;
}
for (ie = node->indirect_calls; ie; ie = ie->next_callee)
{
struct cgraph_node *callee;
- struct inline_summary *isummary;
+ struct ipa_fn_summary *isummary;
enum availability avail;
tree target;
bool speculative;
callee = callee->function_symbol (&avail);
if (avail < AVAIL_AVAILABLE)
continue;
- isummary = inline_summaries->get (callee);
+ isummary = ipa_fn_summaries->get (callee);
if (!isummary->inlinable)
continue;
/* Return time bonus incurred because of HINTS. */
static int
-hint_time_bonus (inline_hints hints)
+hint_time_bonus (ipa_hints hints)
{
int result = 0;
if (hints & (INLINE_HINT_loop_iterations | INLINE_HINT_loop_stride))
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. */
static bool
good_cloning_opportunity_p (struct cgraph_node *node, int time_benefit,
- int freq_sum, gcov_type count_sum, int size_cost)
+ int freq_sum, profile_count count_sum, int size_cost)
{
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);
- if (max_count)
+ struct ipa_node_params *info = IPA_NODE_REF (node);
+ if (max_count > profile_count::zero ())
{
- int factor = (count_sum * 1000) / max_count;
+ int factor = RDIV (count_sum.probability_in
+ (max_count).to_reg_br_prob_base ()
+ * 1000, REG_BR_PROB_BASE);
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
+ {
+ fprintf (dump_file, " good_cloning_opportunity_p (time: %i, "
+ "size: %i, count_sum: ", time_benefit, size_cost);
+ count_sum.dump (dump_file);
+ fprintf (dump_file, "%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);
}
if (removable_params_cost)
*removable_params_cost = 0;
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
ipcp_lattice<tree> *lat = &plats->itself;
*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)
{
perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs_ptrs,
- int base_time, int removable_params_cost,
+ int removable_params_cost,
int est_move_cost, ipcp_value_base *val)
{
- int time, size, time_benefit;
- inline_hints hints;
+ int size, time_benefit;
+ sreal time, base_time;
+ ipa_hints hints;
estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
known_aggs_ptrs, &size, &time,
- &hints);
- time_benefit = base_time - time
+ &base_time, &hints);
+ base_time -= time;
+ if (base_time > 65535)
+ base_time = 65535;
+ time_benefit = base_time.to_int ()
+ devirtualization_time_bonus (node, known_csts, known_contexts,
known_aggs_ptrs)
+ hint_time_bonus (hints)
vec<ipa_agg_jump_function> known_aggs;
vec<ipa_agg_jump_function_p> known_aggs_ptrs;
bool always_const;
- int base_time = inline_summaries->get (node)->time;
int removable_params_cost;
if (!count || !ipcp_versionable_function_p (node))
return;
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "\nEstimating effects for %s/%i, base_time: %i.\n",
- node->name (), node->order, base_time);
+ fprintf (dump_file, "\nEstimating effects for %s.\n", node->dump_name ());
always_const = gather_context_independent_values (info, &known_csts,
&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;
- int time, size;
+ ipa_hints hints;
+ sreal time, base_time;
+ int size;
init_caller_stats (&stats);
node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
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);
+ known_aggs_ptrs, &size, &time,
+ &base_time, &hints);
+ time -= devirt_bonus;
time -= hint_time_bonus (hints);
time -= removable_params_cost;
size -= stats.n_calls * removable_params_cost;
if (dump_file)
fprintf (dump_file, " - context independent values, size: %i, "
- "time_benefit: %i\n", size, base_time - time);
+ "time_benefit: %f\n", size, (base_time - time).to_double ());
- 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;
if (dump_file)
fprintf (dump_file, " Decided to specialize for all "
"known contexts, code not going to grow.\n");
}
- else if (good_cloning_opportunity_p (node, base_time - time,
+ else if (good_cloning_opportunity_p (node,
+ MAX ((base_time - time).to_int (),
+ 65536),
stats.freq_sum, stats.count_sum,
size))
{
if (size + overall_size <= max_new_size)
{
info->do_clone_for_all_contexts = true;
- base_time = time;
overall_size += size;
if (dump_file)
"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++)
+ for (i = 0; i < count; i++)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
ipcp_lattice<tree> *lat = &plats->itself;
int emc = estimate_move_cost (TREE_TYPE (val->value), true);
perform_estimation_of_a_value (node, known_csts, known_contexts,
- known_aggs_ptrs, base_time,
+ known_aggs_ptrs,
removable_params_cost, emc, val);
if (dump_file && (dump_flags & TDF_DETAILS))
{
known_contexts[i] = val->value;
perform_estimation_of_a_value (node, known_csts, known_contexts,
- known_aggs_ptrs, base_time,
+ known_aggs_ptrs,
removable_params_cost, 0, val);
if (dump_file && (dump_flags & TDF_DETAILS))
known_contexts[i] = ipa_polymorphic_call_context ();
}
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
struct ipa_agg_jump_function *ajf;
vec_safe_push (ajf->items, item);
perform_estimation_of_a_value (node, known_csts, known_contexts,
- known_aggs_ptrs, base_time,
+ known_aggs_ptrs,
removable_params_cost, 0, val);
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " - estimates for value ");
print_ipcp_constant_value (dump_file, val->value);
fprintf (dump_file, " for ");
- ipa_dump_param (dump_file, info, i);
+ ipa_dump_param (dump_file, info, i);
fprintf (dump_file, "[%soffset: " HOST_WIDE_INT_PRINT_DEC
"]: time_benefit: %i, size: %i\n",
plats->aggs_by_ref ? "ref " : "",
}
}
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
vec_free (known_aggs[i].items);
known_csts.release ();
struct ipa_node_params *info = IPA_NODE_REF (node);
int i, count = ipa_get_param_count (info);
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
ipcp_lattice<tree> *lat = &plats->itself;
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_across_call (cs))
+ push_node_to_stack (topo, cs->callee->function_symbol ());
+ }
v = pop_node_from_stack (topo);
}
add_all_node_vals_to_toposort (v, topo);
for (cs = v->callees; cs; cs = cs->next_callee)
if (!ipa_edge_within_scc (cs))
- propagate_constants_accross_call (cs);
+ propagate_constants_across_call (cs);
}
cycle_nodes.release ();
}
if (dump_file)
fprintf (dump_file, "\n Propagating constants:\n\n");
- if (in_lto_p)
- ipa_update_after_lto_read ();
-
-
FOR_EACH_DEFINED_FUNCTION (node)
{
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,
initialize_node_lattices (node);
}
if (node->definition && !node->alias)
- overall_size += inline_summaries->get (node)->self_size;
+ overall_size += ipa_fn_summaries->get (node)->self_size;
if (node->count > max_count)
max_count = node->count;
}
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 ();
}
/* Turning calls to direct calls will improve overall summary. */
if (found)
- inline_update_overall_summary (node);
+ ipa_update_overall_fn_summary (node);
}
/* Vector of pointers which for linked lists of clones of an original crgaph
static bool
get_info_about_necessary_edges (ipcp_value<valtype> *val, cgraph_node *dest,
int *freq_sum,
- gcov_type *count_sum, int *caller_count)
+ profile_count *count_sum, int *caller_count)
{
ipcp_value_source<valtype> *src;
int freq = 0, count = 0;
- gcov_type cnt = 0;
+ profile_count cnt = profile_count::zero ();
bool hot = false;
for (src = val->sources; src; src = src->next)
{
count++;
freq += cs->frequency;
- cnt += cs->count;
+ if (cs->count.initialized_p ())
+ cnt += cs->count;
hot |= cs->maybe_hot_p ();
}
cs = get_next_cgraph_edge_clone (cs);
{
fprintf (dump_file, " replacing ");
ipa_dump_param (dump_file, info, parm_num);
-
+
fprintf (dump_file, " with const ");
- print_generic_expr (dump_file, value, 0);
+ print_generic_expr (dump_file, value);
fprintf (dump_file, "\n");
}
replace_map->old_tree = NULL;
{
struct cgraph_edge *cs;
- fprintf (dump_file, " setting count of the specialized node to "
- HOST_WIDE_INT_PRINT_DEC "\n", (HOST_WIDE_INT) new_node->count);
- for (cs = new_node->callees; cs ; cs = cs->next_callee)
- fprintf (dump_file, " edge to %s has count "
- HOST_WIDE_INT_PRINT_DEC "\n",
- cs->callee->name (), (HOST_WIDE_INT) cs->count);
+ fprintf (dump_file, " setting count of the specialized node to ");
+ new_node->count.dump (dump_file);
+ fprintf (dump_file, "\n");
+ for (cs = new_node->callees; cs; cs = cs->next_callee)
+ {
+ fprintf (dump_file, " edge to %s has count ",
+ cs->callee->name ());
+ cs->count.dump (dump_file);
+ fprintf (dump_file, "\n");
+ }
- fprintf (dump_file, " setting count of the original node to "
- HOST_WIDE_INT_PRINT_DEC "\n", (HOST_WIDE_INT) orig_node->count);
- for (cs = orig_node->callees; cs ; cs = cs->next_callee)
- fprintf (dump_file, " edge to %s is left with "
- HOST_WIDE_INT_PRINT_DEC "\n",
- cs->callee->name (), (HOST_WIDE_INT) cs->count);
+ fprintf (dump_file, " setting count of the original node to ");
+ orig_node->count.dump (dump_file);
+ fprintf (dump_file, "\n");
+ for (cs = orig_node->callees; cs; cs = cs->next_callee)
+ {
+ fprintf (dump_file, " edge to %s is left with ",
+ cs->callee->name ());
+ cs->count.dump (dump_file);
+ fprintf (dump_file, "\n");
+ }
}
/* After a specialized NEW_NODE version of ORIG_NODE has been created, update
{
struct cgraph_edge *cs;
struct caller_statistics stats;
- gcov_type new_sum, orig_sum;
- gcov_type remainder, orig_node_count = orig_node->count;
+ profile_count new_sum, orig_sum;
+ profile_count remainder, orig_node_count = orig_node->count;
- if (orig_node_count == 0)
+ if (!(orig_node_count > profile_count::zero ()))
return;
init_caller_stats (&stats);
if (orig_node_count < orig_sum + new_sum)
{
if (dump_file)
- fprintf (dump_file, " Problem: node %s/%i has too low count "
- HOST_WIDE_INT_PRINT_DEC " while the sum of incoming "
- "counts is " HOST_WIDE_INT_PRINT_DEC "\n",
- orig_node->name (), orig_node->order,
- (HOST_WIDE_INT) orig_node_count,
- (HOST_WIDE_INT) (orig_sum + new_sum));
-
- orig_node_count = (orig_sum + new_sum) * 12 / 10;
+ {
+ fprintf (dump_file, " Problem: node %s has too low count ",
+ orig_node->dump_name ());
+ orig_node_count.dump (dump_file);
+ fprintf (dump_file, "while the sum of incoming count is ");
+ (orig_sum + new_sum).dump (dump_file);
+ fprintf (dump_file, "\n");
+ }
+
+ orig_node_count = (orig_sum + new_sum).apply_scale (12, 10);
if (dump_file)
- fprintf (dump_file, " proceeding by pretending it was "
- HOST_WIDE_INT_PRINT_DEC "\n",
- (HOST_WIDE_INT) orig_node_count);
+ {
+ fprintf (dump_file, " proceeding by pretending it was ");
+ orig_node_count.dump (dump_file);
+ fprintf (dump_file, "\n");
+ }
}
new_node->count = new_sum;
remainder = orig_node_count - new_sum;
orig_node->count = remainder;
- for (cs = new_node->callees; cs ; cs = cs->next_callee)
+ for (cs = new_node->callees; cs; cs = cs->next_callee)
+ /* FIXME: why we care about non-zero frequency here? */
if (cs->frequency)
- cs->count = apply_probability (cs->count,
- GCOV_COMPUTE_SCALE (new_sum,
- orig_node_count));
+ cs->count = cs->count.apply_scale (new_sum, orig_node_count);
else
- cs->count = 0;
+ cs->count = profile_count::zero ();
- for (cs = orig_node->callees; cs ; cs = cs->next_callee)
- cs->count = apply_probability (cs->count,
- GCOV_COMPUTE_SCALE (remainder,
- orig_node_count));
+ for (cs = orig_node->callees; cs; cs = cs->next_callee)
+ cs->count = cs->count.apply_scale (remainder, orig_node_count);
if (dump_file)
dump_profile_updates (orig_node, new_node);
static void
update_specialized_profile (struct cgraph_node *new_node,
struct cgraph_node *orig_node,
- gcov_type redirected_sum)
+ profile_count redirected_sum)
{
struct cgraph_edge *cs;
- gcov_type new_node_count, orig_node_count = orig_node->count;
+ profile_count new_node_count, orig_node_count = orig_node->count;
if (dump_file)
- fprintf (dump_file, " the sum of counts of redirected edges is "
- HOST_WIDE_INT_PRINT_DEC "\n", (HOST_WIDE_INT) redirected_sum);
- if (orig_node_count == 0)
+ {
+ fprintf (dump_file, " the sum of counts of redirected edges is ");
+ redirected_sum.dump (dump_file);
+ fprintf (dump_file, "\n");
+ }
+ if (!(orig_node_count > profile_count::zero ()))
return;
gcc_assert (orig_node_count >= redirected_sum);
new_node->count += redirected_sum;
orig_node->count -= redirected_sum;
- for (cs = new_node->callees; cs ; cs = cs->next_callee)
+ for (cs = new_node->callees; cs; cs = cs->next_callee)
if (cs->frequency)
- cs->count += apply_probability (cs->count,
- GCOV_COMPUTE_SCALE (redirected_sum,
- new_node_count));
+ cs->count += cs->count.apply_scale (redirected_sum, new_node_count);
else
- cs->count = 0;
+ cs->count = profile_count::zero ();
- for (cs = orig_node->callees; cs ; cs = cs->next_callee)
+ for (cs = orig_node->callees; cs; cs = cs->next_callee)
{
- gcov_type dec = apply_probability (cs->count,
- GCOV_COMPUTE_SCALE (redirected_sum,
- orig_node_count));
- if (dec < cs->count)
- cs->count -= dec;
- else
- cs->count = 0;
+ profile_count dec = cs->count.apply_scale (redirected_sum,
+ orig_node_count);
+ cs->count -= dec;
}
if (dump_file)
fprintf (dump_file, " cannot change function signature\n");
}
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
{
tree t = known_csts[i];
if (t)
args_to_skip, "constprop");
ipa_set_node_agg_value_chain (new_node, aggvals);
for (av = aggvals; av; av = av->next)
- new_node->maybe_create_reference (av->value, IPA_REF_ADDR, NULL);
+ new_node->maybe_create_reference (av->value, NULL);
if (dump_file && (dump_flags & TDF_DETAILS))
{
- fprintf (dump_file, " the new node is %s/%i.\n",
- new_node->name (), new_node->order);
+ fprintf (dump_file, " the new node is %s.\n", new_node->dump_name ());
if (known_contexts.exists ())
{
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
if (!known_contexts[i].useless_p ())
{
fprintf (dump_file, " known ctx %i is ", i);
struct ipa_node_params *info = IPA_NODE_REF (node);
int i, count = ipa_get_param_count (info);
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
{
struct cgraph_edge *cs;
tree newval = NULL_TREE;
struct ipa_jump_func *jump_func;
tree t;
- if (i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs)))
- {
- newval = NULL_TREE;
- break;
- }
+ 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;
+ }
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
ipa_node_params *info = IPA_NODE_REF (node);
int i, count = ipa_get_param_count (info);
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
{
cgraph_edge *cs;
}
}
-/* Copy agggregate replacement values of NODE (which is an IPA-CP clone) to the
+/* Copy aggregate replacement values of NODE (which is an IPA-CP clone) to the
vector result while subtracting OFFSET from the individual value offsets. */
static vec<ipa_agg_jf_item>
count = c;
}
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
{
struct cgraph_edge *cs;
vec<ipa_agg_jf_item> inter = vNULL;
return res;
}
-/* Turn KNOWN_AGGS into a list of aggreate replacement values. */
+/* Turn KNOWN_AGGS into a list of aggregate replacement values. */
static struct ipa_agg_replacement_value *
known_aggs_to_agg_replacement_list (vec<ipa_agg_jump_function> known_aggs)
/* 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>
perhaps_add_new_callers (cgraph_node *node, ipcp_value<valtype> *val)
{
ipcp_value_source<valtype> *src;
- gcov_type redirected_sum = 0;
+ profile_count redirected_sum = profile_count::zero ();
for (src = val->sources; src; src = src->next)
{
&& cgraph_edge_brings_all_agg_vals_for_node (cs, val->spec_node))
{
if (dump_file)
- fprintf (dump_file, " - adding an extra caller %s/%i"
- " of %s/%i\n",
- xstrdup_for_dump (cs->caller->name ()),
- cs->caller->order,
- xstrdup_for_dump (val->spec_node->name ()),
- val->spec_node->order);
+ fprintf (dump_file, " - adding an extra caller %s of %s\n",
+ cs->caller->dump_name (),
+ val->spec_node->dump_name ());
cs->redirect_callee_duplicating_thunks (val->spec_node);
val->spec_node->expand_all_artificial_thunks ();
- redirected_sum += cs->count;
+ if (cs->count.initialized_p ())
+ redirected_sum = redirected_sum + cs->count;
}
cs = get_next_cgraph_edge_clone (cs);
}
}
- if (redirected_sum)
+ if (redirected_sum > profile_count::zero ())
update_specialized_profile (val->spec_node, node, redirected_sum);
}
{
struct ipa_agg_replacement_value *aggvals;
int freq_sum, caller_count;
- gcov_type count_sum;
+ profile_count count_sum;
vec<cgraph_edge *> callers;
if (val->spec_node)
return false;
if (dump_file)
- fprintf (dump_file, " Creating a specialized node of %s/%i.\n",
- node->name (), node->order);
+ fprintf (dump_file, " Creating a specialized node of %s.\n",
+ node->dump_name ());
callers = gather_edges_for_value (val, node, caller_count);
if (offset == -1)
return false;
if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "\nEvaluating opportunities for %s/%i.\n",
- node->name (), node->order);
+ fprintf (dump_file, "\nEvaluating opportunities for %s.\n",
+ node->dump_name ());
gather_context_independent_values (info, &known_csts, &known_contexts,
info->do_clone_for_all_contexts ? &known_aggs
: NULL, NULL);
- for (i = 0; i < count ;i++)
+ for (i = 0; i < count;i++)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
ipcp_lattice<tree> *lat = &plats->itself;
known_contexts);
}
- info = IPA_NODE_REF (node);
+ info = IPA_NODE_REF (node);
}
if (info->do_clone_for_all_contexts)
vec<cgraph_edge *> callers;
if (dump_file)
- fprintf (dump_file, " - Creating a specialized node of %s/%i "
- "for all known contexts.\n", node->name (),
- node->order);
+ fprintf (dump_file, " - Creating a specialized node of %s "
+ "for all known contexts.\n", node->dump_name ());
callers = node->collect_callers ();
info = IPA_NODE_REF (node);
info->do_clone_for_all_contexts = false;
IPA_NODE_REF (clone)->is_all_contexts_clone = true;
- for (i = 0; i < count ; i++)
+ for (i = 0; i < count; i++)
vec_free (known_aggs[i].items);
known_aggs.release ();
ret = true;
static bool
has_undead_caller_from_outside_scc_p (struct cgraph_node *node,
- void *data ATTRIBUTE_UNUSED)
+ void *data ATTRIBUTE_UNUSED)
{
struct cgraph_edge *cs;
identify_dead_nodes (struct cgraph_node *node)
{
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 ()
+ for (v = node; v; v = ((struct ipa_dfs_info *) v->aux)->next_cycle)
+ 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;
- for (v = node; v ; v = ((struct ipa_dfs_info *) v->aux)->next_cycle)
+ for (v = node; v; v = ((struct ipa_dfs_info *) v->aux)->next_cycle)
if (!IPA_NODE_REF (v)->node_dead)
spread_undeadness (v);
if (dump_file && (dump_flags & TDF_DETAILS))
{
- for (v = node; v ; v = ((struct ipa_dfs_info *) v->aux)->next_cycle)
+ for (v = node; v; v = ((struct ipa_dfs_info *) v->aux)->next_cycle)
if (IPA_NODE_REF (v)->node_dead)
- fprintf (dump_file, " Marking node as dead: %s/%i.\n",
- v->name (), v->order);
+ fprintf (dump_file, " Marking node as dead: %s.\n", v->dump_name ());
}
}
{
struct cgraph_node *v;
iterate = false;
- for (v = node; v ; v = ((struct ipa_dfs_info *) v->aux)->next_cycle)
+ for (v = node; v; v = ((struct ipa_dfs_info *) v->aux)->next_cycle)
if (v->has_gimple_body_p ()
&& ipcp_versionable_function_p (v))
iterate |= decide_whether_version_node (v);
}
}
-/* Look up all alignment information that we have discovered and copy it over
+/* Look up all the bits information that we have discovered and copy it over
to the transformation summary. */
static void
-ipcp_store_alignment_results (void)
+ipcp_store_bits_results (void)
{
cgraph_node *node;
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
- {
- ipa_node_params *info = IPA_NODE_REF (node);
- bool dumped_sth = false;
- bool found_useful_result = false;
-
- if (info->ipcp_orig_node)
- info = IPA_NODE_REF (info->ipcp_orig_node);
-
- unsigned count = ipa_get_param_count (info);
- 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)
- {
- found_useful_result = true;
- break;
- }
- }
- if (!found_useful_result)
- continue;
+ {
+ ipa_node_params *info = IPA_NODE_REF (node);
+ bool dumped_sth = false;
+ bool found_useful_result = false;
- ipcp_grow_transformations_if_necessary ();
- ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
- vec_safe_reserve_exact (ts->alignments, count);
+ if (!opt_for_fn (node->decl, flag_ipa_bit_cp))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Not considering %s for ipa bitwise propagation "
+ "; -fipa-bit-cp: disabled.\n",
+ node->name ());
+ continue;
+ }
- for (unsigned i = 0; i < count ; i++)
- {
- ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+ if (info->ipcp_orig_node)
+ info = IPA_NODE_REF (info->ipcp_orig_node);
- if (plats->alignment.align == 0)
- plats->alignment.known = false;
+ unsigned count = ipa_get_param_count (info);
+ for (unsigned i = 0; i < count; i++)
+ {
+ ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+ if (plats->bits_lattice.constant_p ())
+ {
+ found_useful_result = true;
+ break;
+ }
+ }
- ts->alignments->quick_push (plats->alignment);
- if (!dump_file || !plats->alignment.known)
- continue;
- if (!dumped_sth)
- {
- fprintf (dump_file, "Propagated alignment info for function %s/%i:\n",
- node->name (), node->order);
- dumped_sth = true;
- }
- fprintf (dump_file, " param %i: align: %u, misalign: %u\n",
- i, plats->alignment.align, plats->alignment.misalign);
- }
- }
+ if (!found_useful_result)
+ continue;
+
+ ipcp_grow_transformations_if_necessary ();
+ ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
+ vec_safe_reserve_exact (ts->bits, count);
+
+ for (unsigned i = 0; i < count; i++)
+ {
+ ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+ ipa_bits *jfbits;
+
+ if (plats->bits_lattice.constant_p ())
+ jfbits
+ = ipa_get_ipa_bits_for_value (plats->bits_lattice.get_value (),
+ plats->bits_lattice.get_mask ());
+ else
+ jfbits = NULL;
+
+ ts->bits->quick_push (jfbits);
+ if (!dump_file || !jfbits)
+ continue;
+ if (!dumped_sth)
+ {
+ fprintf (dump_file, "Propagated bits info for function %s:\n",
+ node->dump_name ());
+ dumped_sth = true;
+ }
+ fprintf (dump_file, " param %i: value = ", i);
+ print_hex (jfbits->value, dump_file);
+ fprintf (dump_file, ", mask = ");
+ print_hex (jfbits->mask, dump_file);
+ fprintf (dump_file, "\n");
+ }
+ }
+}
+
+/* Look up all VR information that we have discovered and copy it over
+ to the transformation summary. */
+
+static void
+ipcp_store_vr_results (void)
+{
+ cgraph_node *node;
+
+ FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+ {
+ ipa_node_params *info = IPA_NODE_REF (node);
+ bool found_useful_result = false;
+
+ if (!opt_for_fn (node->decl, flag_ipa_vrp))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Not considering %s for VR discovery "
+ "and propagate; -fipa-ipa-vrp: disabled.\n",
+ node->name ());
+ continue;
+ }
+
+ if (info->ipcp_orig_node)
+ info = IPA_NODE_REF (info->ipcp_orig_node);
+
+ unsigned count = ipa_get_param_count (info);
+ for (unsigned i = 0; i < count; i++)
+ {
+ ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+ if (!plats->m_value_range.bottom_p ()
+ && !plats->m_value_range.top_p ())
+ {
+ found_useful_result = true;
+ break;
+ }
+ }
+ if (!found_useful_result)
+ continue;
+
+ ipcp_grow_transformations_if_necessary ();
+ ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
+ vec_safe_reserve_exact (ts->m_vr, count);
+
+ for (unsigned i = 0; i < count; i++)
+ {
+ ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+ ipa_vr vr;
+
+ if (!plats->m_value_range.bottom_p ()
+ && !plats->m_value_range.top_p ())
+ {
+ vr.known = true;
+ vr.type = plats->m_value_range.m_vr.type;
+ vr.min = wi::to_wide (plats->m_value_range.m_vr.min);
+ vr.max = wi::to_wide (plats->m_value_range.m_vr.max);
+ }
+ else
+ {
+ vr.known = false;
+ vr.type = VR_VARYING;
+ vr.min = vr.max = wi::zero (INT_TYPE_SIZE);
+ }
+ ts->m_vr->quick_push (vr);
+ }
+ }
}
/* The IPCP driver. */
ipa_check_create_node_params ();
ipa_check_create_edge_args ();
grow_edge_clone_vectors ();
- edge_duplication_hook_holder =
- symtab->add_edge_duplication_hook (&ipcp_edge_duplication_hook, NULL);
- 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);
+ edge_duplication_hook_holder
+ = symtab->add_edge_duplication_hook (&ipcp_edge_duplication_hook, NULL);
+ edge_removal_hook_holder
+ = symtab->add_edge_removal_hook (&ipcp_edge_removal_hook, NULL);
+
if (dump_file)
{
fprintf (dump_file, "\nIPA structures before propagation:\n");
if (dump_flags & TDF_DETAILS)
- ipa_print_all_params (dump_file);
+ ipa_print_all_params (dump_file);
ipa_print_all_jump_functions (dump_file);
}
ipcp_propagate_stage (&topo);
/* Decide what constant propagation and cloning should be performed. */
ipcp_decision_stage (&topo);
- /* Store results of alignment propagation. */
- ipcp_store_alignment_results ();
+ /* Store results of bits propagation. */
+ ipcp_store_bits_results ();
+ /* Store results of value range propagation. */
+ ipcp_store_vr_results ();
/* 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. */
void
ipa_cp_c_finalize (void)
{
- max_count = 0;
+ max_count = profile_count::zero ();
overall_size = 0;
max_new_size = 0;
}