+/* Propagate constants from the caller to the callee of CS. INFO describes the
+ caller. */
+
+static bool
+propagate_constants_accross_call (struct cgraph_edge *cs)
+{
+ struct ipa_node_params *callee_info;
+ enum availability availability;
+ struct cgraph_node *callee, *alias_or_thunk;
+ struct ipa_edge_args *args;
+ bool ret = false;
+ int i, args_count, parms_count;
+
+ callee = cgraph_function_node (cs->callee, &availability);
+ if (!callee->analyzed)
+ return false;
+ gcc_checking_assert (cgraph_function_with_gimple_body_p (callee));
+ callee_info = IPA_NODE_REF (callee);
+
+ args = IPA_EDGE_REF (cs);
+ args_count = ipa_get_cs_argument_count (args);
+ parms_count = ipa_get_param_count (callee_info);
+
+ /* If this call goes through a thunk we must not propagate to the first (0th)
+ parameter. However, we might need to uncover a thunk from below a series
+ of aliases first. */
+ alias_or_thunk = cs->callee;
+ while (alias_or_thunk->alias)
+ alias_or_thunk = cgraph_alias_aliased_node (alias_or_thunk);
+ if (alias_or_thunk->thunk.thunk_p)
+ {
+ ret |= set_lattice_contains_variable (ipa_get_lattice (callee_info, 0));
+ i = 1;
+ }
+ else
+ i = 0;
+
+ for (; (i < args_count) && (i < parms_count); i++)
+ {
+ struct ipa_jump_func *jump_func = ipa_get_ith_jump_func (args, i);
+ struct ipcp_lattice *dest_lat = ipa_get_lattice (callee_info, i);
+
+ if (availability == AVAIL_OVERWRITABLE)
+ ret |= set_lattice_contains_variable (dest_lat);
+ else
+ ret |= propagate_accross_jump_function (cs, jump_func, dest_lat);
+ }
+ for (; i < parms_count; i++)
+ ret |= set_lattice_contains_variable (ipa_get_lattice (callee_info, i));
+
+ return ret;
+}
+
+/* If an indirect edge IE can be turned into a direct one based on KNOWN_VALS
+ (which can contain both constants and binfos) or KNOWN_BINFOS (which can be
+ NULL) return the destination. */
+
+tree
+ipa_get_indirect_edge_target (struct cgraph_edge *ie,
+ VEC (tree, heap) *known_vals,
+ VEC (tree, heap) *known_binfos)
+{
+ int param_index = ie->indirect_info->param_index;
+ HOST_WIDE_INT token, anc_offset;
+ tree otr_type;
+ tree t;
+
+ if (param_index == -1)
+ return NULL_TREE;
+
+ if (!ie->indirect_info->polymorphic)
+ {
+ tree t = (VEC_length (tree, known_vals) > (unsigned int) param_index
+ ? VEC_index (tree, known_vals, param_index) : NULL);
+ if (t &&
+ TREE_CODE (t) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL)
+ return TREE_OPERAND (t, 0);
+ else
+ return NULL_TREE;
+ }
+
+ token = ie->indirect_info->otr_token;
+ anc_offset = ie->indirect_info->anc_offset;
+ otr_type = ie->indirect_info->otr_type;
+
+ t = VEC_index (tree, known_vals, param_index);
+ if (!t && known_binfos
+ && VEC_length (tree, known_binfos) > (unsigned int) param_index)
+ t = VEC_index (tree, known_binfos, param_index);
+ if (!t)
+ return NULL_TREE;
+
+ if (TREE_CODE (t) != TREE_BINFO)
+ {
+ tree binfo;
+ binfo = gimple_extract_devirt_binfo_from_cst (t);
+ if (!binfo)
+ return NULL_TREE;
+ binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
+ if (!binfo)
+ return NULL_TREE;
+ return gimple_get_virt_method_for_binfo (token, binfo);
+ }
+ else
+ {
+ tree binfo;
+
+ binfo = get_binfo_at_offset (t, anc_offset, otr_type);
+ if (!binfo)
+ return NULL_TREE;
+ return gimple_get_virt_method_for_binfo (token, binfo);
+ }
+}
+
+/* Calculate devirtualization time bonus for NODE, assuming we know KNOWN_CSTS
+ and KNOWN_BINFOS. */
+
+static int
+devirtualization_time_bonus (struct cgraph_node *node,
+ VEC (tree, heap) *known_csts,
+ VEC (tree, heap) *known_binfos)
+{
+ struct cgraph_edge *ie;
+ int res = 0;
+
+ for (ie = node->indirect_calls; ie; ie = ie->next_callee)
+ {
+ struct cgraph_node *callee;
+ struct inline_summary *isummary;
+ tree target;
+
+ target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos);
+ if (!target)
+ continue;
+
+ /* Only bare minimum benefit for clearly un-inlineable targets. */
+ res += 1;
+ callee = cgraph_get_node (target);
+ if (!callee || !callee->analyzed)
+ continue;
+ isummary = inline_summary (callee);
+ if (!isummary->inlinable)
+ continue;
+
+ /* FIXME: The values below need re-considering and perhaps also
+ integrating into the cost metrics, at lest in some very basic way. */
+ if (isummary->size <= MAX_INLINE_INSNS_AUTO / 4)
+ res += 31;
+ else if (isummary->size <= MAX_INLINE_INSNS_AUTO / 2)
+ res += 15;
+ else if (isummary->size <= MAX_INLINE_INSNS_AUTO
+ || DECL_DECLARED_INLINE_P (callee->decl))
+ res += 7;
+ }
+
+ return res;
+}
+
+/* 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)
+{
+ if (time_benefit == 0
+ || !flag_ipa_cp_clone
+ || !optimize_function_for_speed_p (DECL_STRUCT_FUNCTION (node->decl)))
+ return false;
+
+ gcc_assert (size_cost > 0);
+
+ if (max_count)
+ {
+ int factor = (count_sum * 1000) / max_count;
+ HOST_WIDEST_INT evaluation = (((HOST_WIDEST_INT) time_benefit * factor)
+ / size_cost);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " good_cloning_opportunity_p (time: %i, "
+ "size: %i, count_sum: " HOST_WIDE_INT_PRINT_DEC
+ ") -> evaluation: " HOST_WIDEST_INT_PRINT_DEC
+ ", threshold: %i\n",
+ time_benefit, size_cost, (HOST_WIDE_INT) count_sum,
+ evaluation, 500);
+
+ return evaluation >= PARAM_VALUE (PARAM_IPA_CP_EVAL_THRESHOLD);
+ }
+ else
+ {
+ HOST_WIDEST_INT evaluation = (((HOST_WIDEST_INT) time_benefit * freq_sum)
+ / size_cost);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " good_cloning_opportunity_p (time: %i, "
+ "size: %i, freq_sum: %i) -> evaluation: "
+ HOST_WIDEST_INT_PRINT_DEC ", threshold: %i\n",
+ time_benefit, size_cost, freq_sum, evaluation,
+ CGRAPH_FREQ_BASE /2);
+
+ return evaluation >= PARAM_VALUE (PARAM_IPA_CP_EVAL_THRESHOLD);
+ }
+}
+
+
+/* Allocate KNOWN_CSTS and KNOWN_BINFOS and populate them with values of
+ parameters that are known independent of the context. INFO describes the
+ function. If REMOVABLE_PARAMS_COST is non-NULL, the movement cost of all
+ removable parameters will be stored in it. */
+
+static bool
+gather_context_independent_values (struct ipa_node_params *info,
+ VEC (tree, heap) **known_csts,
+ VEC (tree, heap) **known_binfos,
+ int *removable_params_cost)