/* 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 "alias.h"
+#include "backend.h"
#include "tree.h"
-#include "options.h"
-#include "fold-const.h"
-#include "gimple-fold.h"
#include "gimple-expr.h"
-#include "target.h"
-#include "backend.h"
#include "predict.h"
-#include "hard-reg-set.h"
-#include "cgraph.h"
#include "alloc-pool.h"
-#include "symbol-summary.h"
-#include "ipa-prop.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"
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. */
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; "
}
if (misalign != (new_misalign % align))
{
- int diff = abs (misalign - (new_misalign % align));
- align = MIN (align, (unsigned) diff & -diff);
+ int diff = abs ((int) misalign - (int) (new_misalign % align));
+ align = (unsigned) diff & -diff;
if (align)
misalign = misalign % align;
else
{
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);
}
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));
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;
}
{
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);
*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 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;
{
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;
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. */