Commit message: Avoid segfault when doing IPA-VRP but not IPA-CP (PR 93015)

2019-12-21  Martin Jambor  <mjambor@suse.cz>

	PR ipa/93015
	* ipa-cp.c (ipcp_store_vr_results): Check that info exists

	testsuite/
	* gcc.dg/lto/pr93015_0.c: New test.

From-SVN: r279695
CVE: None
CWE: CWE-20
Vulnerability description: None

Project: gcc

  bool bottom_p () { return m_lattice_val == IPA_BITS_VARYING; }

Project: gcc

ipcp_value<valtype>::add_source (cgraph_edge *cs, ipcp_value *src_val,
				 int src_idx, HOST_WIDE_INT offset)
{
  ipcp_value_source<valtype> *src;

  src = new (ipcp_sources_pool.allocate ()) ipcp_value_source<valtype>;
  src->offset = offset;
  src->cs = cs;
  src->val = src_val;
  src->index = src_idx;

  src->next = sources;
  sources = src;
}

Project: gcc

modify_known_vectors_with_val (vec<tree> *known_csts,
			       vec<ipa_polymorphic_call_context> *known_contexts,
			       ipcp_value<ipa_polymorphic_call_context> *val,
			       int index)
{
  *known_csts = known_csts->copy ();
  *known_contexts = known_contexts->copy ();
  (*known_contexts)[index] = val->value;
}

Project: gcc

value_topo_info<valtype>::add_val (ipcp_value<valtype> *cur_val)
{
  ipcp_value_source<valtype> *src;

  if (cur_val->dfs)
    return;

  dfs_counter++;
  cur_val->dfs = dfs_counter;
  cur_val->low_link = dfs_counter;

  cur_val->topo_next = stack;
  stack = cur_val;
  cur_val->on_stack = true;

  for (src = cur_val->sources; src; src = src->next)
    if (src->val)
      {
	if (src->val->dfs == 0)
	  {
	    add_val (src->val);
	    if (src->val->low_link < cur_val->low_link)
	      cur_val->low_link = src->val->low_link;
	  }
	else if (src->val->on_stack
		 && src->val->dfs < cur_val->low_link)
	  cur_val->low_link = src->val->dfs;
      }

  if (cur_val->dfs == cur_val->low_link)
    {
      ipcp_value<valtype> *v, *scc_list = NULL;

      do
	{
	  v = stack;
	  stack = v->topo_next;
	  v->on_stack = false;

	  v->scc_next = scc_list;
	  scc_list = v;
	}
      while (v != cur_val);

      cur_val->topo_next = values_topo;
      values_topo = cur_val;
    }
}

Project: gcc

ipa_get_indirect_edge_target (struct cgraph_edge *ie,
			      vec<tree> known_csts,
			      vec<ipa_polymorphic_call_context> known_contexts,
			      vec<ipa_agg_value_set> known_aggs,
			      bool *speculative)
{
  return ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts,
					 known_aggs, NULL, speculative);
}

Project: gcc

propagate_constants_across_call (struct cgraph_edge *cs)
{
  class ipa_node_params *callee_info;
  enum availability availability;
  cgraph_node *callee;
  class ipa_edge_args *args;
  bool ret = false;
  int i, args_count, parms_count;

  callee = cs->callee->function_symbol (&availability);
  if (!callee->definition)
    return false;
  gcc_checking_assert (callee->has_gimple_body_p ());
  callee_info = IPA_NODE_REF (callee);
  if (!callee_info)
    return false;

  args = IPA_EDGE_REF (cs);
  parms_count = ipa_get_param_count (callee_info);
  if (parms_count == 0)
    return false;
  if (!args
      || !opt_for_fn (cs->caller->decl, flag_ipa_cp)
      || !opt_for_fn (cs->caller->decl, optimize))
    {
      for (i = 0; i < parms_count; i++)
	ret |= set_all_contains_variable (ipa_get_parm_lattices (callee_info,
								 i));
      return ret;
    }
  args_count = ipa_get_cs_argument_count (args);

  /* 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.  */
  if (call_passes_through_thunk_p (cs))
    {
      ret |= set_all_contains_variable (ipa_get_parm_lattices (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);
      class 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_across_jump_function (cs, jump_func,
							&dest_plats->itself,
							param_type);
	  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++)
    ret |= set_all_contains_variable (ipa_get_parm_lattices (callee_info, i));

  return ret;
}

Project: gcc

get_info_about_necessary_edges (ipcp_value<valtype> *val, cgraph_node *dest,
				int *freq_sum,
				profile_count *count_sum, int *caller_count)
{
  ipcp_value_source<valtype> *src;
  int freq = 0, count = 0;
  profile_count cnt = profile_count::zero ();
  bool hot = false;
  bool non_self_recursive = false;

  for (src = val->sources; src; src = src->next)
    {
      struct cgraph_edge *cs = src->cs;
      while (cs)
	{
	  if (cgraph_edge_brings_value_p (cs, src, dest, val))
	    {
	      count++;
	      freq += cs->frequency ();
	      if (cs->count.ipa ().initialized_p ())
	        cnt += cs->count.ipa ();
	      hot |= cs->maybe_hot_p ();
	      if (cs->caller != dest)
		non_self_recursive = true;
	      else if (src->val)
		gcc_assert (values_equal_for_ipcp_p (src->val->value,
						     val->value));
	    }
	  cs = get_next_cgraph_edge_clone (cs);
	}
    }

  /* If the only edges bringing a value are self-recursive ones, do not bother
     evaluating it.  */
  if (!non_self_recursive)
    return false;

  *freq_sum = freq;
  *count_sum = cnt;
  *caller_count = count;

  if (!hot && IPA_NODE_REF (dest)->node_within_scc)
    {
      struct cgraph_edge *cs;

      /* Cold non-SCC source edge could trigger hot recursive execution of
	 function. Consider the case as hot and rely on following cost model
	 computation to further select right one.  */
      for (cs = dest->callers; cs; cs = cs->next_caller)
	if (cs->caller == dest && cs->maybe_hot_p ())
	  return true;
    }

  return hot;
}

Project: gcc

  ipcp_value()
    : sources (0), next (0), scc_next (0), topo_next (0),
      spec_node (0), dfs (0), low_link (0), on_stack (false) {}

Project: gcc

get_next_cgraph_edge_clone (struct cgraph_edge *cs)
{
  edge_clone_summary *s = edge_clone_summaries->get (cs);
  return s != NULL ? s->next_clone : NULL;
}

Project: gcc

intersect_with_agg_replacements (struct cgraph_node *node, int index,
				 vec<ipa_agg_value> *inter,
				 HOST_WIDE_INT offset)
{
  struct ipa_agg_replacement_value *srcvals;
  struct ipa_agg_value *item;
  int i;

  srcvals = ipa_get_agg_replacements_for_node (node);
  if (!srcvals)
    {
      inter->release ();
      return;
    }

  FOR_EACH_VEC_ELT (*inter, i, item)
    {
      struct ipa_agg_replacement_value *av;
      bool found = false;
      if (!item->value)
	continue;
      for (av = srcvals; av; av = av->next)
	{
	  gcc_checking_assert (av->value);
	  if (av->index == index
	      && av->offset - offset == item->offset)
	    {
	      if (values_equal_for_ipcp_p (item->value, av->value))
		found = true;
	      else if (item->value == error_mark_node)
		{
		  /* Replace place holder value with real one.  */
		  item->value = av->value;
		  found = true;
		}
	      break;
	    }
	}
      if (!found)
	item->value = NULL_TREE;
    }
}

Project: gcc

incorporate_penalties (ipa_node_params *info, int64_t evaluation)
{
  if (info->node_within_scc && !info->node_is_self_scc)
    evaluation = (evaluation
		  * (100 - param_ipa_cp_recursion_penalty)) / 100;

  if (info->node_calling_single_call)
    evaluation = (evaluation
		  * (100 - param_ipa_cp_single_call_penalty))
      / 100;

  return evaluation;
}

Project: gcc

ipcp_bits_lattice::set_to_bottom ()
{
  if (bottom_p ())
    return false;
  m_lattice_val = IPA_BITS_VARYING;
  m_value = 0;
  m_mask = -1;
  return true;
}

Project: gcc

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->local
	&& IPA_NODE_REF (v)
	&& !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)
    if (IPA_NODE_REF (v) && !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)
	if (IPA_NODE_REF (v) && IPA_NODE_REF (v)->node_dead)
	  fprintf (dump_file, "  Marking node as dead: %s.\n", v->dump_name ());
    }
}

Project: gcc

merge_agg_lats_step (class ipcp_param_lattices *dest_plats,
		     HOST_WIDE_INT offset, HOST_WIDE_INT val_size,
		     struct ipcp_agg_lattice ***aglat,
		     bool pre_existing, bool *change)
{
  gcc_checking_assert (offset >= 0);

  while (**aglat && (**aglat)->offset < offset)
    {
      if ((**aglat)->offset + (**aglat)->size > offset)
	{
	  set_agg_lats_to_bottom (dest_plats);
	  return false;
	}
      *change |= (**aglat)->set_contains_variable ();
      *aglat = &(**aglat)->next;
    }

  if (**aglat && (**aglat)->offset == offset)
    {
      if ((**aglat)->size != val_size)
	{
	  set_agg_lats_to_bottom (dest_plats);
	  return false;
	}
      gcc_assert (!(**aglat)->next
		  || (**aglat)->next->offset >= offset + val_size);
      return true;
    }
  else
    {
      struct ipcp_agg_lattice *new_al;

      if (**aglat && (**aglat)->offset < offset + val_size)
	{
	  set_agg_lats_to_bottom (dest_plats);
	  return false;
	}
      if (dest_plats->aggs_count == param_ipa_max_agg_items)
	return false;
      dest_plats->aggs_count++;
      new_al = ipcp_agg_lattice_pool.allocate ();
      memset (new_al, 0, sizeof (*new_al));

      new_al->offset = offset;
      new_al->size = val_size;
      new_al->contains_variable = pre_existing;

      new_al->next = **aglat;
      **aglat = new_al;
      return true;
    }
}

Project: gcc

ignore_edge_p (cgraph_edge *e)
{
  enum availability avail;
  cgraph_node *ultimate_target
    = e->callee->function_or_virtual_thunk_symbol (&avail, e->caller);

  return (avail <= AVAIL_INTERPOSABLE
	  || !opt_for_fn (ultimate_target->decl, optimize)
	  || !opt_for_fn (ultimate_target->decl, flag_ipa_cp));
}

Project: gcc

ipcp_vr_lattice::bottom_p () const
{
  return m_vr.varying_p ();
}

Project: gcc

  void init () { gcc_assert (m_vr.undefined_p ()); }

Project: gcc

values_equal_for_ipcp_p (ipa_polymorphic_call_context x,
			 ipa_polymorphic_call_context y)
{
  return x.equal_to (y);
}

Project: gcc

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 (!info || !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);
      if (!info->lattices)
	/* Newly expanded artificial thunks do not have lattices.  */
	continue;

      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_transformation_initialize ();
      ipcp_transformation *ts = ipcp_transformation_sum->get_create (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.kind ();
	      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);
	}
    }
}

Project: gcc

ipcp_lattice<valtype>::is_single_const ()
{
  if (bottom || contains_variable || values_count != 1)
    return false;
  else
    return true;
}

Project: gcc

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 cannot
       be optimized out, count it as a real use.  */
    if (!cs->caller->thunk.thunk_p || !cs->caller->local)
      ++*caller_count;
  return false;
}

Project: gcc

ipcp_verify_propagated_values (void)
{
  struct cgraph_node *node;

  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
    {
      class ipa_node_params *info = IPA_NODE_REF (node);
      if (!opt_for_fn (node->decl, flag_ipa_cp)
	  || !opt_for_fn (node->decl, optimize))
	continue;
      int i, count = ipa_get_param_count (info);

      for (i = 0; i < count; i++)
	{
	  ipcp_lattice<tree> *lat = ipa_get_scalar_lat (info, i);

	  if (!lat->bottom
	      && !lat->contains_variable
	      && lat->values_count == 0)
	    {
	      if (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);
		}

	      gcc_unreachable ();
	    }
	}
    }
}

Project: gcc

  virtual bool gate (function *)
    {
      /* FIXME: We should remove the optimize check after we ensure we never run
	 IPA passes when not optimizing.  */
      return (flag_ipa_cp && optimize) || in_lto_p;
    }

Project: gcc

push_node_to_stack (class ipa_topo_info *topo, struct cgraph_node *node)
{
  class ipa_node_params *info = IPA_NODE_REF (node);
  if (info->node_enqueued)
    return;
  info->node_enqueued = 1;
  topo->stack[topo->stack_top++] = node;
}

Project: gcc

ipa_agg_value_set_from_jfunc (class ipa_node_params *info, cgraph_node *node,
			      struct ipa_agg_jump_function *agg_jfunc)
{
  struct ipa_agg_value_set agg;
  struct ipa_agg_jf_item *item;
  int i;

  agg.items = vNULL;
  agg.by_ref = agg_jfunc->by_ref;

  FOR_EACH_VEC_SAFE_ELT (agg_jfunc->items, i, item)
    {
      tree value = ipa_agg_value_from_node (info, node, item);

      if (value)
	{
	  struct ipa_agg_value value_item;

	  value_item.offset = item->offset;
	  value_item.value = value;

	  agg.items.safe_push (value_item);
	}
    }
  return agg;
}

Project: gcc

decide_about_value (struct cgraph_node *node, int index, HOST_WIDE_INT offset,
		    ipcp_value<valtype> *val, vec<tree> known_csts,
		    vec<ipa_polymorphic_call_context> known_contexts)
{
  struct ipa_agg_replacement_value *aggvals;
  int freq_sum, caller_count;
  profile_count count_sum;
  vec<cgraph_edge *> callers;

  if (val->spec_node)
    {
      perhaps_add_new_callers (node, val);
      return false;
    }
  else if (val->local_size_cost + overall_size > max_new_size)
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
	fprintf (dump_file, "   Ignoring candidate value because "
		 "max_new_size would be reached with %li.\n",
		 val->local_size_cost + overall_size);
      return false;
    }
  else if (!get_info_about_necessary_edges (val, node, &freq_sum, &count_sum,
					    &caller_count))
    return false;

  if (dump_file && (dump_flags & TDF_DETAILS))
    {
      fprintf (dump_file, " - considering value ");
      print_ipcp_constant_value (dump_file, val->value);
      fprintf (dump_file, " for ");
      ipa_dump_param (dump_file, IPA_NODE_REF (node), index);
      if (offset != -1)
	fprintf (dump_file, ", offset: " HOST_WIDE_INT_PRINT_DEC, offset);
      fprintf (dump_file, " (caller_count: %i)\n", caller_count);
    }

  if (!good_cloning_opportunity_p (node, val->local_time_benefit,
				   freq_sum, count_sum,
				   val->local_size_cost)
      && !good_cloning_opportunity_p (node,
				      val->local_time_benefit
				      + val->prop_time_benefit,
				      freq_sum, count_sum,
				      val->local_size_cost
				      + val->prop_size_cost))
    return false;

  if (dump_file)
    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)
    modify_known_vectors_with_val (&known_csts, &known_contexts, val, index);
  else
    {
      known_csts = known_csts.copy ();
      known_contexts = copy_useful_known_contexts (known_contexts);
    }
  find_more_scalar_values_for_callers_subset (node, known_csts, callers);
  find_more_contexts_for_caller_subset (node, &known_contexts, callers);
  aggvals = find_aggregate_values_for_callers_subset (node, callers);
  gcc_checking_assert (ipcp_val_agg_replacement_ok_p (aggvals, index,
						      offset, val->value));
  val->spec_node = create_specialized_node (node, known_csts, known_contexts,
					    aggvals, callers);
  overall_size += val->local_size_cost;

  /* TODO: If for some lattice there is only one other known value
     left, make a special node for it too. */

  return true;
}

Project: gcc

allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
{
  ipcp_value<ipa_polymorphic_call_context> *val;

  // TODO
  val = new (ipcp_poly_ctx_values_pool.allocate ())
    ipcp_value<ipa_polymorphic_call_context>();
  val->value = source;
  return val;
}

Project: gcc

make_pass_ipa_cp (gcc::context *ctxt)
{
  return new pass_ipa_cp (ctxt);
}

Project: gcc

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 (!opt_for_fn (node->decl, flag_ipa_bit_cp) || !info)
	{
	  if (dump_file)
	    fprintf (dump_file, "Not considering %s for ipa bitwise propagation "
				"; -fipa-bit-cp: disabled.\n",
				node->name ());
	  continue;
	}

      if (info->ipcp_orig_node)
	info = IPA_NODE_REF (info->ipcp_orig_node);
      if (!info->lattices)
	/* Newly expanded artificial thunks do not have lattices.  */
	continue;

      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;
	    }
	}

      if (!found_useful_result)
	continue;

      ipcp_transformation_initialize ();
      ipcp_transformation *ts = ipcp_transformation_sum->get_create (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");
	}
    }
}

Project: gcc

allocate_and_init_ipcp_value (tree source)
{
  ipcp_value<tree> *val;

  val = new (ipcp_cst_values_pool.allocate ()) ipcp_value<tree>();
  val->value = source;
  return val;
}

Project: gcc

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);
      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);
}

Project: gcc

set_agg_lats_contain_variable (class ipcp_param_lattices *plats)
{
  bool ret = !plats->aggs_contain_variable;
  plats->aggs_contain_variable = true;
  return ret;
}

Project: gcc

set_all_contains_variable (class ipcp_param_lattices *plats)
{
  bool ret;
  ret = plats->itself.set_contains_variable ();
  ret |= plats->ctxlat.set_contains_variable ();
  ret |= set_agg_lats_contain_variable (plats);
  ret |= plats->bits_lattice.set_to_bottom ();
  ret |= plats->m_value_range.set_to_bottom ();
  return ret;
}

Project: gcc

find_more_contexts_for_caller_subset (cgraph_node *node,
				      vec<ipa_polymorphic_call_context>
				      *known_contexts,
				      vec<cgraph_edge *> callers)
{
  ipa_node_params *info = IPA_NODE_REF (node);
  int i, count = ipa_get_param_count (info);

  for (i = 0; i < count; i++)
    {
      cgraph_edge *cs;

      if (ipa_get_poly_ctx_lat (info, i)->bottom
	  || (known_contexts->exists ()
	      && !(*known_contexts)[i].useless_p ()))
	continue;

      ipa_polymorphic_call_context newval;
      bool first = true;
      int j;

      FOR_EACH_VEC_ELT (callers, j, cs)
	{
	  if (!IPA_EDGE_REF (cs)
	      || i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs)))
	    return;
	  ipa_jump_func *jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs),
							    i);
	  ipa_polymorphic_call_context ctx;
	  ctx = ipa_context_from_jfunc (IPA_NODE_REF (cs->caller), cs, i,
					jfunc);
	  if (first)
	    {
	      newval = ctx;
	      first = false;
	    }
	  else
	    newval.meet_with (ctx);
	  if (newval.useless_p ())
	    break;
	}

      if (!newval.useless_p ())
	{
	  if (dump_file && (dump_flags & TDF_DETAILS))
	    {
	      fprintf (dump_file, "    adding an extra known polymorphic "
		       "context ");
	      print_ipcp_constant_value (dump_file, newval);
	      fprintf (dump_file, " for ");
	      ipa_dump_param (dump_file, info, i);
	      fprintf (dump_file, "\n");
	    }

	  if (!known_contexts->exists ())
	    known_contexts->safe_grow_cleared (ipa_get_param_count (info));
	  (*known_contexts)[i] = newval;
	}

    }
}

Project: gcc

ipcp_driver (void)
{
  class ipa_topo_info topo;

  if (edge_clone_summaries == NULL)
    edge_clone_summaries = new edge_clone_summary_t (symtab);

  ipa_check_create_node_params ();
  ipa_check_create_edge_args ();
  clone_num_suffixes = new hash_map<const char *, unsigned>;

  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_jump_functions (dump_file);
    }

  /* Topological sort.  */
  build_toporder_info (&topo);
  /* Do the interprocedural propagation.  */
  ipcp_propagate_stage (&topo);
  /* Decide what constant propagation and cloning should be performed.  */
  ipcp_decision_stage (&topo);
  /* Store results of bits propagation.  */
  ipcp_store_bits_results ();
  /* Store results of value range propagation.  */
  ipcp_store_vr_results ();

  /* Free all IPCP structures.  */
  delete clone_num_suffixes;
  free_toporder_info (&topo);
  delete edge_clone_summaries;
  edge_clone_summaries = NULL;
  ipa_free_all_structures_after_ipa_cp ();
  if (dump_file)
    fprintf (dump_file, "\nIPA constant propagation end\n");
  return 0;
}

Project: gcc

ipcp_vr_lattice::top_p () const
{
  return m_vr.undefined_p ();
}

Project: gcc

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;
}

Project: gcc

ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
				vec<tree> known_csts,
				vec<ipa_polymorphic_call_context> known_contexts,
				vec<ipa_agg_value_set> known_aggs,
				struct ipa_agg_replacement_value *agg_reps,
				bool *speculative)
{
  int param_index = ie->indirect_info->param_index;
  HOST_WIDE_INT anc_offset;
  tree t = NULL;
  tree target = NULL;

  *speculative = false;

  if (param_index == -1)
    return NULL_TREE;

  if (!ie->indirect_info->polymorphic)
    {
      tree t = NULL;

      if (ie->indirect_info->agg_contents)
	{
	  t = NULL;
	  if (agg_reps && ie->indirect_info->guaranteed_unmodified)
	    {
	      while (agg_reps)
		{
		  if (agg_reps->index == param_index
		      && agg_reps->offset == ie->indirect_info->offset
		      && agg_reps->by_ref == ie->indirect_info->by_ref)
		    {
		      t = agg_reps->value;
		      break;
		    }
		  agg_reps = agg_reps->next;
		}
	    }
	  if (!t)
	    {
	      struct ipa_agg_value_set *agg;
	      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,
					      (unsigned) param_index
						 < known_csts.length ()
					      ? known_csts[param_index]
					      : NULL,
					      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 if ((unsigned) param_index < known_csts.length ())
	t = known_csts[param_index];

      if (t
	  && TREE_CODE (t) == ADDR_EXPR
	  && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL)
	return TREE_OPERAND (t, 0);
      else
	return NULL_TREE;
    }

  if (!opt_for_fn (ie->caller->decl, flag_devirtualize))
    return NULL_TREE;

  gcc_assert (!ie->indirect_info->agg_contents);
  anc_offset = ie->indirect_info->offset;

  t = NULL;

  /* Try to work out value of virtual table pointer value in replacements.  */
  if (!t && agg_reps && !ie->indirect_info->by_ref)
    {
      while (agg_reps)
	{
	  if (agg_reps->index == param_index
	      && agg_reps->offset == ie->indirect_info->offset
	      && agg_reps->by_ref)
	    {
	      t = agg_reps->value;
	      break;
	    }
	  agg_reps = agg_reps->next;
	}
    }

  /* Try to work out value of virtual table pointer value in known
     aggregate values.  */
  if (!t && known_aggs.length () > (unsigned int) param_index
      && !ie->indirect_info->by_ref)
    {
      struct ipa_agg_value_set *agg = &known_aggs[param_index];
      t = ipa_find_agg_cst_for_param (agg,
				      (unsigned) param_index
					 < known_csts.length ()
				      ? known_csts[param_index] : NULL,
				      ie->indirect_info->offset, true);
    }

  /* If we found the virtual table pointer, lookup the target.  */
  if (t)
    {
      tree vtable;
      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, &can_refer);
	  if (can_refer)
	    {
	      if (!target
		  || fndecl_built_in_p (target, BUILT_IN_UNREACHABLE)
		  || !possible_polymorphic_call_target_p
		       (ie, cgraph_node::get (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;
	    }
	}
    }

  /* Do we know the constant value of pointer?  */
  if (!t && (unsigned) param_index < known_csts.length ())
    t = known_csts[param_index];

  gcc_checking_assert (!t || TREE_CODE (t) != TREE_BINFO);

  ipa_polymorphic_call_context context;
  if (known_contexts.length () > (unsigned int) param_index)
    {
      context = known_contexts[param_index];
      context.offset_by (anc_offset);
      if (ie->indirect_info->vptr_changed)
	context.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
					      ie->indirect_info->otr_type);
      if (t)
	{
	  ipa_polymorphic_call_context ctx2 = ipa_polymorphic_call_context
	    (t, ie->indirect_info->otr_type, anc_offset);
	  if (!ctx2.useless_p ())
	    context.combine_with (ctx2, ie->indirect_info->otr_type);
	}
    }
  else if (t)
    {
      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;

  vec <cgraph_node *>targets;
  bool final;

  targets = possible_polymorphic_call_targets
    (ie->indirect_info->otr_type,
     ie->indirect_info->otr_token,
     context, &final);
  if (!final || targets.length () > 1)
    {
      struct cgraph_node *node;
      if (*speculative)
	return target;
      if (!opt_for_fn (ie->caller->decl, flag_devirtualize_speculatively)
	  || ie->speculative || !ie->maybe_hot_p ())
	return NULL;
      node = try_speculative_devirtualization (ie->indirect_info->otr_type,
					       ie->indirect_info->otr_token,
					       context);
      if (node)
	{
	  *speculative = true;
	  target = node->decl;
	}
      else
	return NULL;
    }
  else
    {
      *speculative = false;
      if (targets.length () == 1)
	target = targets[0]->decl;
      else
	target = ipa_impossible_devirt_target (ie, NULL_TREE);
    }

  if (target && !possible_polymorphic_call_target_p (ie,
						     cgraph_node::get (target)))
    {
      if (*speculative)
	return NULL;
      target = ipa_impossible_devirt_target (ie, target);
    }

  return target;
}

Project: gcc

ipcp_propagate_stage (class ipa_topo_info *topo)
{
  struct cgraph_node *node;

  if (dump_file)
    fprintf (dump_file, "\n Propagating constants:\n\n");

  max_count = profile_count::uninitialized ();

  FOR_EACH_DEFINED_FUNCTION (node)
  {
    if (node->has_gimple_body_p ()
	&& opt_for_fn (node->decl, flag_ipa_cp)
	&& opt_for_fn (node->decl, optimize))
      {
        class ipa_node_params *info = IPA_NODE_REF (node);
        determine_versionability (node, info);
	info->lattices = XCNEWVEC (class ipcp_param_lattices,
				   ipa_get_param_count (info));
	initialize_node_lattices (node);
      }
    ipa_size_summary *s = ipa_size_summaries->get (node);
    if (node->definition && !node->alias && s != NULL)
      overall_size += s->self_size;
    max_count = max_count.max (node->count.ipa ());
  }

  max_new_size = overall_size;
  if (max_new_size < param_large_unit_insns)
    max_new_size = param_large_unit_insns;
  max_new_size += max_new_size * param_ipcp_unit_growth / 100 + 1;

  if (dump_file)
    fprintf (dump_file, "\noverall_size: %li, max_new_size: %li\n",
	     overall_size, max_new_size);

  propagate_constants_topo (topo);
  if (flag_checking)
    ipcp_verify_propagated_values ();
  topo->constants.propagate_effects ();
  topo->contexts.propagate_effects ();

  if (dump_file)
    {
      fprintf (dump_file, "\nIPA lattices after all propagation:\n");
      print_all_lattices (dump_file, (dump_flags & TDF_DETAILS), true);
    }
}

Project: gcc

  ~edge_clone_summary ()
  {
    if (prev_clone)
      edge_clone_summaries->get (prev_clone)->next_clone = next_clone;
    if (next_clone)
      edge_clone_summaries->get (next_clone)->prev_clone = prev_clone;
  }

Project: gcc

perhaps_add_new_callers (cgraph_node *node, ipcp_value<valtype> *val)
{
  ipcp_value_source<valtype> *src;
  profile_count redirected_sum = profile_count::zero ();

  for (src = val->sources; src; src = src->next)
    {
      struct cgraph_edge *cs = src->cs;
      while (cs)
	{
	  if (cgraph_edge_brings_value_p (cs, src, node, val)
	      && cgraph_edge_brings_all_scalars_for_node (cs, val->spec_node)
	      && cgraph_edge_brings_all_agg_vals_for_node (cs, val->spec_node))
	    {
	      if (dump_file)
		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 ();
	      if (cs->count.ipa ().initialized_p ())
	        redirected_sum = redirected_sum + cs->count.ipa ();
	    }
	  cs = get_next_cgraph_edge_clone (cs);
	}
    }

  if (redirected_sum.nonzero_p ())
    update_specialized_profile (val->spec_node, node, redirected_sum);
}

Project: gcc

ipa_agg_value_from_node (class ipa_node_params *info,
			 struct cgraph_node *node,
			 struct ipa_agg_jf_item *item)
{
  tree value = NULL_TREE;
  int src_idx;

  if (item->offset < 0 || item->jftype == IPA_JF_UNKNOWN)
    return NULL_TREE;

  if (item->jftype == IPA_JF_CONST)
    return item->value.constant;

  gcc_checking_assert (item->jftype == IPA_JF_PASS_THROUGH
		       || item->jftype == IPA_JF_LOAD_AGG);

  src_idx = item->value.pass_through.formal_id;

  if (info->ipcp_orig_node)
    {
      if (item->jftype == IPA_JF_PASS_THROUGH)
	value = info->known_csts[src_idx];
      else
	value = get_clone_agg_value (node, item->value.load_agg.offset,
				     src_idx);
    }
  else if (info->lattices)
    {
      class ipcp_param_lattices *src_plats
	= ipa_get_parm_lattices (info, src_idx);

      if (item->jftype == IPA_JF_PASS_THROUGH)
	{
	  struct ipcp_lattice<tree> *lat = &src_plats->itself;

	  if (!lat->is_single_const ())
	    return NULL_TREE;

	  value = lat->values->value;
	}
      else if (src_plats->aggs
	       && !src_plats->aggs_bottom
	       && !src_plats->aggs_contain_variable
	       && src_plats->aggs_by_ref == item->value.load_agg.by_ref)
	{
	  struct ipcp_agg_lattice *aglat;

	  for (aglat = src_plats->aggs; aglat; aglat = aglat->next)
	    {
	      if (aglat->offset > item->value.load_agg.offset)
		break;

	      if (aglat->offset == item->value.load_agg.offset)
		{
		  if (aglat->is_single_const ())
		    value = aglat->values->value;
		  break;
		}
	    }
	}
    }

  if (!value)
    return NULL_TREE;

  if (item->jftype == IPA_JF_LOAD_AGG)
    {
      tree load_type = item->value.load_agg.type;
      tree value_type = TREE_TYPE (value);

      /* Ensure value type is compatible with load type.  */
      if (!useless_type_conversion_p (load_type, value_type))
	return NULL_TREE;
    }

  return ipa_get_jf_arith_result (item->value.pass_through.operation,
				  value,
				  item->value.pass_through.operand,
				  item->type);
}

Project: gcc

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;
}

Project: gcc

set_chain_of_aglats_contains_variable (struct ipcp_agg_lattice *aglat)
{
  bool ret = false;
  while (aglat)
    {
      ret |= aglat->set_contains_variable ();
      aglat = aglat->next;
    }
  return ret;
}

Project: gcc

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;
    }
}

Project: gcc

gather_context_independent_values (class ipa_node_params *info,
				   vec<tree> *known_csts,
				   vec<ipa_polymorphic_call_context>
				   *known_contexts,
				   vec<ipa_agg_value_set> *known_aggs,
				   int *removable_params_cost)
{
  int i, count = ipa_get_param_count (info);
  bool ret = false;

  known_csts->create (0);
  known_contexts->create (0);
  known_csts->safe_grow_cleared (count);
  known_contexts->safe_grow_cleared (count);
  if (known_aggs)
    {
      known_aggs->create (0);
      known_aggs->safe_grow_cleared (count);
    }

  if (removable_params_cost)
    *removable_params_cost = 0;

  for (i = 0; i < count; i++)
    {
      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
      ipcp_lattice<tree> *lat = &plats->itself;

      if (lat->is_single_const ())
	{
	  ipcp_value<tree> *val = lat->values;
	  gcc_checking_assert (TREE_CODE (val->value) != TREE_BINFO);
	  (*known_csts)[i] = val->value;
	  if (removable_params_cost)
	    *removable_params_cost
	      += estimate_move_cost (TREE_TYPE (val->value), false);
	  ret = true;
	}
      else if (removable_params_cost
	       && !ipa_is_param_used (info, i))
	*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;

      if (known_aggs)
	{
	  vec<ipa_agg_value> agg_items;
	  struct ipa_agg_value_set *agg;

	  agg_items = context_independent_aggregate_values (plats);
	  agg = &(*known_aggs)[i];
	  agg->items = agg_items;
	  agg->by_ref = plats->aggs_by_ref;
	  ret |= !agg_items.is_empty ();
	}
    }

  return ret;
}

Project: gcc

intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
				vec<ipa_agg_value> inter)
{
  struct ipa_jump_func *jfunc;
  jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), index);
  if (jfunc->type == IPA_JF_PASS_THROUGH
      && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
    {
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      int src_idx = ipa_get_jf_pass_through_formal_id (jfunc);

      if (caller_info->ipcp_orig_node)
	{
	  struct cgraph_node *orig_node = caller_info->ipcp_orig_node;
	  class ipcp_param_lattices *orig_plats;
	  orig_plats = ipa_get_parm_lattices (IPA_NODE_REF (orig_node),
					      src_idx);
	  if (agg_pass_through_permissible_p (orig_plats, jfunc))
	    {
	      if (!inter.exists ())
		inter = agg_replacements_to_vector (cs->caller, src_idx, 0);
	      else
		intersect_with_agg_replacements (cs->caller, src_idx,
						 &inter, 0);
	    }
	  else
	    {
	      inter.release ();
	      return vNULL;
	    }
	}
      else
	{
	  class ipcp_param_lattices *src_plats;
	  src_plats = ipa_get_parm_lattices (caller_info, src_idx);
	  if (agg_pass_through_permissible_p (src_plats, jfunc))
	    {
	      /* Currently we do not produce clobber aggregate jump
		 functions, adjust when we do.  */
	      gcc_checking_assert (!jfunc->agg.items);
	      if (!inter.exists ())
		inter = copy_plats_to_inter (src_plats, 0);
	      else
		intersect_with_plats (src_plats, &inter, 0);
	    }
	  else
	    {
	      inter.release ();
	      return vNULL;
	    }
	}
    }
  else if (jfunc->type == IPA_JF_ANCESTOR
	   && ipa_get_jf_ancestor_agg_preserved (jfunc))
    {
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      int src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
      class ipcp_param_lattices *src_plats;
      HOST_WIDE_INT delta = ipa_get_jf_ancestor_offset (jfunc);

      if (caller_info->ipcp_orig_node)
	{
	  if (!inter.exists ())
	    inter = agg_replacements_to_vector (cs->caller, src_idx, delta);
	  else
	    intersect_with_agg_replacements (cs->caller, src_idx, &inter,
					     delta);
	}
      else
	{
	  src_plats = ipa_get_parm_lattices (caller_info, src_idx);
	  /* Currently we do not produce clobber aggregate jump
	     functions, adjust when we do.  */
	  gcc_checking_assert (!src_plats->aggs || !jfunc->agg.items);
	  if (!inter.exists ())
	    inter = copy_plats_to_inter (src_plats, delta);
	  else
	    intersect_with_plats (src_plats, &inter, delta);
	}
    }
  else if (jfunc->agg.items)
    {
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      struct ipa_agg_value *item;
      int k;

      if (!inter.exists ())
	for (unsigned i = 0; i < jfunc->agg.items->length (); i++)
	  {
	    struct ipa_agg_jf_item *agg_item = &(*jfunc->agg.items)[i];
	    struct ipa_agg_value agg_value;

	    if (self_recursive_agg_pass_through_p (cs, agg_item, index))
	      {
		/* For a self-recursive call, if aggregate jump function is a
		   simple pass-through, the exact value that it stands for is
		   not known at this point, which must comes from other call
		   sites.  But we still need to add a place holder in value
		   sets to indicate it, here we use error_mark_node to
		   represent the special unknown value, which will be replaced
		   with real one during later intersecting operations.  */
		agg_value.value = error_mark_node;
	      }
	    else
	      {
		tree value = ipa_agg_value_from_node (caller_info, cs->caller,
						      agg_item);
		if (!value)
		  continue;

		agg_value.value = value;
	      }

	    agg_value.offset = agg_item->offset;
	    inter.safe_push (agg_value);
	  }
      else
	FOR_EACH_VEC_ELT (inter, k, item)
	  {
	    int l = 0;
	    bool found = false;

	    if (!item->value)
	      continue;

	    while ((unsigned) l < jfunc->agg.items->length ())
	      {
		struct ipa_agg_jf_item *ti;
		ti = &(*jfunc->agg.items)[l];
		if (ti->offset > item->offset)
		  break;
		if (ti->offset == item->offset)
		  {
		    tree value;

		    if (self_recursive_agg_pass_through_p (cs, ti, index))
		      {
			/* A simple aggregate pass-through in self-recursive
			   call should lead to same value.  */
			found = true;
		      }
		    else if ((value = ipa_agg_value_from_node (caller_info,
							     cs->caller, ti)))
		      {
			if (values_equal_for_ipcp_p (item->value, value))
			  found = true;
			else if (item->value == error_mark_node)
			  {
			    /* Replace unknown place holder value with real
			       one.  */
			    item->value = value;
			    found = true;
			  }
		      }
		    break;
		  }
		l++;
	      }
	    if (!found)
	      item->value = NULL;
	  }
    }
  else
    {
      inter.release ();
      return vNULL;
    }
  return inter;
}

Project: gcc

ipcp_val_agg_replacement_ok_p (ipa_agg_replacement_value *,
			       int , HOST_WIDE_INT offset,
			       ipa_polymorphic_call_context)
{
  return offset == -1;
}

Project: gcc

modify_known_vectors_with_val (vec<tree> *known_csts,
			       vec<ipa_polymorphic_call_context> *known_contexts,
			       ipcp_value<tree> *val,
			       int index)
{
  *known_csts = known_csts->copy ();
  *known_contexts = copy_useful_known_contexts (*known_contexts);
  (*known_csts)[index] = val->value;
}

Project: gcc

gather_caller_stats (struct cgraph_node *node, void *data)
{
  struct caller_statistics *stats = (struct caller_statistics *) data;
  struct cgraph_edge *cs;

  for (cs = node->callers; cs; cs = cs->next_caller)
    if (!cs->caller->thunk.thunk_p)
      {
        if (cs->count.ipa ().initialized_p ())
	  stats->count_sum += cs->count.ipa ();
	stats->freq_sum += cs->frequency ();
	stats->n_calls++;
	if (cs->maybe_hot_p ())
	  stats->n_hot_calls ++;
      }
  return false;

}

Project: gcc

devirtualization_time_bonus (struct cgraph_node *node,
			     vec<tree> known_csts,
			     vec<ipa_polymorphic_call_context> known_contexts,
			     vec<ipa_agg_value_set> known_aggs)
{
  struct cgraph_edge *ie;
  int res = 0;

  for (ie = node->indirect_calls; ie; ie = ie->next_callee)
    {
      struct cgraph_node *callee;
      class ipa_fn_summary *isummary;
      enum availability avail;
      tree target;
      bool speculative;

      target = ipa_get_indirect_edge_target (ie, known_csts, known_contexts,
					     known_aggs, &speculative);
      if (!target)
	continue;

      /* Only bare minimum benefit for clearly un-inlineable targets.  */
      res += 1;
      callee = cgraph_node::get (target);
      if (!callee || !callee->definition)
	continue;
      callee = callee->function_symbol (&avail);
      if (avail < AVAIL_AVAILABLE)
	continue;
      isummary = ipa_fn_summaries->get (callee);
      if (!isummary->inlinable)
	continue;

      int size = ipa_size_summaries->get (callee)->size;
      /* FIXME: The values below need re-considering and perhaps also
	 integrating into the cost metrics, at lest in some very basic way.  */
      int max_inline_insns_auto
	= opt_for_fn (callee->decl, param_max_inline_insns_auto);
      if (size <= max_inline_insns_auto / 4)
	res += 31 / ((int)speculative + 1);
      else if (size <= max_inline_insns_auto / 2)
	res += 15 / ((int)speculative + 1);
      else if (size <= max_inline_insns_auto
	       || DECL_DECLARED_INLINE_P (callee->decl))
	res += 7 / ((int)speculative + 1);
    }

  return res;
}

Project: gcc

same_node_or_its_all_contexts_clone_p (cgraph_node *node, cgraph_node *dest)
{
  if (node == dest)
    return true;

  class ipa_node_params *info = IPA_NODE_REF (node);
  return info->is_all_contexts_clone && info->ipcp_orig_node == dest;
}

Project: gcc

ipcp_vr_lattice::set_to_bottom ()
{
  if (m_vr.varying_p ())
    return false;
  /* ?? We create all sorts of VARYING ranges for floats, structures,
     and other types which we cannot handle as ranges.  We should
     probably avoid handling them throughout the pass, but it's easier
     to create a sensible VARYING here and let the lattice
     propagate.  */
  m_vr.set_varying (integer_type_node);
  return true;
}

Project: gcc

ipa_value_range_from_jfunc (ipa_node_params *info, cgraph_edge *cs,
			    ipa_jump_func *jfunc, tree parm_type)
{
  value_range vr;
  return vr;
  if (jfunc->m_vr)
    ipa_vr_operation_and_type_effects (&vr,
				       jfunc->m_vr,
				       NOP_EXPR, parm_type,
				       jfunc->m_vr->type ());
  if (vr.singleton_p ())
    return vr;
  if (jfunc->type == IPA_JF_PASS_THROUGH)
    {
      int idx;
      ipcp_transformation *sum
	= ipcp_get_transformation_summary (cs->caller->inlined_to
					   ? cs->caller->inlined_to
					   : cs->caller);
      if (!sum || !sum->m_vr)
	return vr;

      idx = ipa_get_jf_pass_through_formal_id (jfunc);

      if (!(*sum->m_vr)[idx].known)
	return vr;
      tree vr_type = ipa_get_type (info, idx);
      value_range srcvr (wide_int_to_tree (vr_type, (*sum->m_vr)[idx].min),
			 wide_int_to_tree (vr_type, (*sum->m_vr)[idx].max),
			 (*sum->m_vr)[idx].type);

      enum tree_code operation = ipa_get_jf_pass_through_operation (jfunc);

      if (TREE_CODE_CLASS (operation) == tcc_unary)
	{
	  value_range res;

	  if (ipa_vr_operation_and_type_effects (&res,
						 &srcvr,
						 operation, parm_type,
						 vr_type))
	    vr.intersect (res);
	}
      else
	{
	  value_range op_res, res;
	  tree op = ipa_get_jf_pass_through_operand (jfunc);
	  value_range op_vr (op, op);

	  range_fold_binary_expr (&op_res, operation, vr_type, &srcvr, &op_vr);
	  if (ipa_vr_operation_and_type_effects (&res,
						 &op_res,
						 NOP_EXPR, parm_type,
						 vr_type))
	    vr.intersect (res);
	}
    }
  return vr;
}

Project: gcc

propagate_constants_topo (class ipa_topo_info *topo)
{
  int i;

  for (i = topo->nnodes - 1; i >= 0; i--)
    {
      unsigned j;
      struct cgraph_node *v, *node = topo->order[i];
      vec<cgraph_node *> cycle_nodes = ipa_get_nodes_in_cycle (node);

      /* First, iteratively propagate within the strongly connected component
	 until all lattices stabilize.  */
      FOR_EACH_VEC_ELT (cycle_nodes, j, v)
	if (v->has_gimple_body_p ())
	  {
	    if (opt_for_fn (v->decl, flag_ipa_cp)
		&& opt_for_fn (v->decl, optimize))
	      push_node_to_stack (topo, v);
	    /* When V is not optimized, we can not push it to stack, but
	       still we need to set all its callees lattices to bottom.  */
	    else
	      {
		for (cgraph_edge *cs = v->callees; cs; cs = cs->next_callee)
	           propagate_constants_across_call (cs);
	      }
	  }

      v = pop_node_from_stack (topo);
      while (v)
	{
	  struct cgraph_edge *cs;
	  class ipa_node_params *info = NULL;
	  bool self_scc = true;

	  for (cs = v->callees; cs; cs = cs->next_callee)
	    if (ipa_edge_within_scc (cs))
	      {
		cgraph_node *callee = cs->callee->function_symbol ();

		if (v != callee)
		  self_scc = false;

		if (!info)
		  {
		    info = IPA_NODE_REF (v);
		    info->node_within_scc = true;
		  }

		if (propagate_constants_across_call (cs))
		  push_node_to_stack (topo, callee);
	      }

	  if (info)
	    info->node_is_self_scc = self_scc;

	  v = pop_node_from_stack (topo);
	}

      /* Afterwards, propagate along edges leading out of the SCC, calculates
	 the local effects of the discovered constants and all valid values to
	 their topological sort.  */
      FOR_EACH_VEC_ELT (cycle_nodes, j, v)
	if (v->has_gimple_body_p ()
	    && opt_for_fn (v->decl, flag_ipa_cp)
	    && opt_for_fn (v->decl, optimize))
	  {
	    struct cgraph_edge *cs;

	    estimate_local_effects (v);
	    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_across_call (cs);
	  }
      cycle_nodes.release ();
    }
}

Project: gcc

ipcp_generate_summary (void)
{
  struct cgraph_node *node;

  if (dump_file)
    fprintf (dump_file, "\nIPA constant propagation start:\n");
  ipa_register_cgraph_hooks ();

  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
    ipa_analyze_node (node);
}

Project: gcc

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");
    }
}

Project: gcc

free_toporder_info (class ipa_topo_info *topo)
{
  ipa_free_postorder_info ();
  free (topo->order);
  free (topo->stack);
}

Project: gcc

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,
				    tree parm_type)
{
  return propagate_vals_across_arith_jfunc (cs,
				ipa_get_jf_pass_through_operation (jfunc),
				NULL_TREE,
				ipa_get_jf_pass_through_operand (jfunc),
				src_lat, dest_lat, -1, src_idx, parm_type);
}

Project: gcc

  widest_int get_mask () { return m_mask; }

Project: gcc

ipcp_read_summary (void)
{
  ipa_prop_read_jump_functions ();
}

Project: gcc

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;

  if (ipa_edge_within_scc (cs))
    return dest_lat->set_contains_variable ();

  for (src_val = src_lat->values; src_val; src_val = src_val->next)
    {
      tree t = ipa_get_jf_ancestor_result (jfunc, src_val->value);

      if (t)
	ret |= dest_lat->add_value (t, cs, src_val, src_idx);
      else
	ret |= dest_lat->set_contains_variable ();
    }

  return ret;
}

Project: gcc

estimate_local_effects (struct cgraph_node *node)
{
  class ipa_node_params *info = IPA_NODE_REF (node);
  int i, count = ipa_get_param_count (info);
  vec<tree> known_csts;
  vec<ipa_polymorphic_call_context> known_contexts;
  vec<ipa_agg_value_set> known_aggs;
  bool always_const;
  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.\n", node->dump_name ());

  always_const = gather_context_independent_values (info, &known_csts,
						    &known_contexts, &known_aggs,
						    &removable_params_cost);
  int devirt_bonus = devirtualization_time_bonus (node, known_csts,
					   known_contexts, known_aggs);
  if (always_const || devirt_bonus
      || (removable_params_cost && node->can_change_signature))
    {
      struct caller_statistics stats;
      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, &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: %f\n", size, (base_time - time).to_double ());

      if (size <= 0 || node->local)
	{
	  info->do_clone_for_all_contexts = true;

	  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,
					   MIN ((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;
	      overall_size += size;

	      if (dump_file)
		fprintf (dump_file, "     Decided to specialize for all "
			 "known contexts, growth deemed beneficial.\n");
	    }
	  else if (dump_file && (dump_flags & TDF_DETAILS))
	    fprintf (dump_file, "   Not cloning for all contexts because "
		     "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++)
    {
      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
      ipcp_lattice<tree> *lat = &plats->itself;
      ipcp_value<tree> *val;

      if (lat->bottom
	  || !lat->values
	  || known_csts[i])
	continue;

      for (val = lat->values; val; val = val->next)
	{
	  gcc_checking_assert (TREE_CODE (val->value) != TREE_BINFO);
	  known_csts[i] = val->value;

	  int emc = estimate_move_cost (TREE_TYPE (val->value), true);
	  perform_estimation_of_a_value (node, known_csts, known_contexts,
					 known_aggs,
					 removable_params_cost, emc, 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);
	      fprintf (dump_file, ": time_benefit: %i, size: %i\n",
		       val->local_time_benefit, val->local_size_cost);
	    }
	}
      known_csts[i] = NULL_TREE;
    }

  for (i = 0; i < count; i++)
    {
      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);

      if (!plats->virt_call)
	continue;

      ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
      ipcp_value<ipa_polymorphic_call_context> *val;

      if (ctxlat->bottom
	  || !ctxlat->values
	  || !known_contexts[i].useless_p ())
	continue;

      for (val = ctxlat->values; val; val = val->next)
	{
	  known_contexts[i] = val->value;
	  perform_estimation_of_a_value (node, known_csts, known_contexts,
					 known_aggs,
					 removable_params_cost, 0, val);

	  if (dump_file && (dump_flags & TDF_DETAILS))
	    {
	      fprintf (dump_file, " - estimates for polymorphic context ");
	      print_ipcp_constant_value (dump_file, val->value);
	      fprintf (dump_file, " for ");
	      ipa_dump_param (dump_file, info, i);
	      fprintf (dump_file, ": time_benefit: %i, size: %i\n",
		       val->local_time_benefit, val->local_size_cost);
	    }
	}
      known_contexts[i] = ipa_polymorphic_call_context ();
    }

  for (i = 0; i < count; i++)
    {
      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
      struct ipa_agg_value_set *agg;
      struct ipcp_agg_lattice *aglat;

      if (plats->aggs_bottom || !plats->aggs)
	continue;

      agg = &known_aggs[i];
      for (aglat = plats->aggs; aglat; aglat = aglat->next)
	{
	  ipcp_value<tree> *val;
	  if (aglat->bottom || !aglat->values
	      /* If the following is true, the one value is in known_aggs.  */
	      || (!plats->aggs_contain_variable
		  && aglat->is_single_const ()))
	    continue;

	  for (val = aglat->values; val; val = val->next)
	    {
	      struct ipa_agg_value item;

	      item.offset = aglat->offset;
	      item.value = val->value;
	      agg->items.safe_push (item);

	      perform_estimation_of_a_value (node, known_csts, known_contexts,
					     known_aggs,
					     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);
		  fprintf (dump_file, "[%soffset: " HOST_WIDE_INT_PRINT_DEC
			   "]: time_benefit: %i, size: %i\n",
			   plats->aggs_by_ref ? "ref " : "",
			   aglat->offset,
			   val->local_time_benefit, val->local_size_cost);
		}

	      agg->items.pop ();
	    }
	}
    }

  known_csts.release ();
  known_contexts.release ();
  ipa_release_agg_values (known_aggs);
}

Project: gcc

cgraph_edge_brings_value_p (cgraph_edge *cs,
			    ipcp_value_source<ipa_polymorphic_call_context> *src,
			    cgraph_node *dest,
			    ipcp_value<ipa_polymorphic_call_context> *)
{
  class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
  enum availability avail;
  cgraph_node *real_dest = cs->callee->function_symbol (&avail);

  if (avail <= AVAIL_INTERPOSABLE
      || !same_node_or_its_all_contexts_clone_p (real_dest, dest)
      || caller_info->node_dead)
    return false;
  if (!src->val)
    return true;

  if (caller_info->ipcp_orig_node)
    return (caller_info->known_contexts.length () > (unsigned) src->index)
      && values_equal_for_ipcp_p (src->val->value,
				  caller_info->known_contexts[src->index]);

  class ipcp_param_lattices *plats = ipa_get_parm_lattices (caller_info,
							     src->index);
  return plats->ctxlat.is_single_const ()
    && values_equal_for_ipcp_p (src->val->value,
				plats->ctxlat.values->value);
}

Project: gcc

propagate_vals_across_arith_jfunc (cgraph_edge *cs,
				   enum tree_code opcode,
				   tree opnd1_type,
				   tree opnd2,
				   ipcp_lattice<tree> *src_lat,
				   ipcp_lattice<tree> *dest_lat,
				   HOST_WIDE_INT src_offset,
				   int src_idx,
				   tree res_type)
{
  ipcp_value<tree> *src_val;
  bool ret = false;

  /* Due to circular dependencies, propagating within an SCC through arithmetic
     transformation would create infinite number of values.  But for
     self-feeding recursive function, we could allow propagation in a limited
     count, and this can enable a simple kind of recursive function versioning.
     For other scenario, we would just make lattices bottom.  */
  if (opcode != NOP_EXPR && ipa_edge_within_scc (cs))
    {
      int i;

      if (src_lat != dest_lat || param_ipa_cp_max_recursive_depth < 1)
	return dest_lat->set_contains_variable ();

      /* No benefit if recursive execution is in low probability.  */
      if (cs->sreal_frequency () * 100
	  <= ((sreal) 1) * param_ipa_cp_min_recursive_probability)
	return dest_lat->set_contains_variable ();

      auto_vec<ipcp_value<tree> *, 8> val_seeds;

      for (src_val = src_lat->values; src_val; src_val = src_val->next)
	{
	  /* Now we do not use self-recursively generated value as propagation
	     source, this is absolutely conservative, but could avoid explosion
	     of lattice's value space, especially when one recursive function
	     calls another recursive.  */
	  if (self_recursively_generated_p (src_val))
	    {
	      ipcp_value_source<tree> *s;

	      /* If the lattice has already been propagated for the call site,
		 no need to do that again.  */
	      for (s = src_val->sources; s; s = s->next)
		if (s->cs == cs)
		  return dest_lat->set_contains_variable ();
	    }
	  else
	    val_seeds.safe_push (src_val);
	}

      /* Recursively generate lattice values with a limited count.  */
      FOR_EACH_VEC_ELT (val_seeds, i, src_val)
	{
	  for (int j = 1; j < param_ipa_cp_max_recursive_depth; j++)
	    {
	      tree cstval = get_val_across_arith_op (opcode, opnd1_type, opnd2,
						     src_val, res_type);
	      if (!cstval)
		break;

	      ret |= dest_lat->add_value (cstval, cs, src_val, src_idx,
					  src_offset, &src_val, true);
	      gcc_checking_assert (src_val);
	    }
	}
      ret |= dest_lat->set_contains_variable ();
    }
  else
    for (src_val = src_lat->values; src_val; src_val = src_val->next)
      {
	/* Now we do not use self-recursively generated value as propagation
	   source, otherwise it is easy to make value space of normal lattice
	   overflow.  */
	if (self_recursively_generated_p (src_val))
	  {
	    ret |= dest_lat->set_contains_variable ();
	    continue;
	  }

	tree cstval = get_val_across_arith_op (opcode, opnd1_type, opnd2,
					       src_val, res_type);
	if (cstval)
	  ret |= dest_lat->add_value (cstval, cs, src_val, src_idx,
				      src_offset);
	else
	  ret |= dest_lat->set_contains_variable ();
      }

  return ret;
}

Project: gcc

print_ipcp_constant_value (FILE * f, ipa_polymorphic_call_context v)
{
  v.dump(f, false);
}

Project: gcc

ipa_vr_operation_and_type_effects (value_range *dst_vr,
				   value_range *src_vr,
				   enum tree_code operation,
				   tree dst_type, tree src_type)
{
  range_fold_unary_expr (dst_vr, operation, dst_type, src_vr, src_type);
  if (dst_vr->varying_p () || dst_vr->undefined_p ())
    return false;
  return true;
}

Project: gcc

ipcp_lattice<valtype>::set_contains_variable ()
{
  bool ret = !contains_variable;
  contains_variable = true;
  return ret;
}

Project: gcc

create_specialized_node (struct cgraph_node *node,
			 vec<tree> known_csts,
			 vec<ipa_polymorphic_call_context> known_contexts,
			 struct ipa_agg_replacement_value *aggvals,
			 vec<cgraph_edge *> callers)
{
  class ipa_node_params *new_info, *info = IPA_NODE_REF (node);
  vec<ipa_replace_map *, va_gc> *replace_trees = NULL;
  vec<ipa_adjusted_param, va_gc> *new_params = NULL;
  struct ipa_agg_replacement_value *av;
  struct cgraph_node *new_node;
  int i, count = ipa_get_param_count (info);
  ipa_param_adjustments *old_adjustments = node->clone.param_adjustments;
  ipa_param_adjustments *new_adjustments;
  gcc_assert (!info->ipcp_orig_node);
  gcc_assert (node->can_change_signature
	      || !old_adjustments);

  if (old_adjustments)
    {
      /* At the moment all IPA optimizations should use the number of
	 parameters of the prevailing decl as the m_always_copy_start.
	 Handling any other value would complicate the code below, so for the
	 time bing let's only assert it is so.  */
      gcc_assert (old_adjustments->m_always_copy_start == count
		  || old_adjustments->m_always_copy_start < 0);
      int old_adj_count = vec_safe_length (old_adjustments->m_adj_params);
      for (i = 0; i < old_adj_count; i++)
	{
	  ipa_adjusted_param *old_adj = &(*old_adjustments->m_adj_params)[i];
	  if (!node->can_change_signature
	      || old_adj->op != IPA_PARAM_OP_COPY
	      || (!known_csts[old_adj->base_index]
		  && ipa_is_param_used (info, old_adj->base_index)))
	    {
	      ipa_adjusted_param new_adj = *old_adj;

	      new_adj.prev_clone_adjustment = true;
	      new_adj.prev_clone_index = i;
	      vec_safe_push (new_params, new_adj);
	    }
	}
      bool skip_return = old_adjustments->m_skip_return;
      new_adjustments = (new (ggc_alloc <ipa_param_adjustments> ())
			 ipa_param_adjustments (new_params, count,
						skip_return));
    }
  else if (node->can_change_signature
	   && want_remove_some_param_p (node, known_csts))
    {
      ipa_adjusted_param adj;
      memset (&adj, 0, sizeof (adj));
      adj.op = IPA_PARAM_OP_COPY;
      for (i = 0; i < count; i++)
	if (!known_csts[i] && ipa_is_param_used (info, i))
	  {
	    adj.base_index = i;
	    adj.prev_clone_index = i;
	    vec_safe_push (new_params, adj);
	  }
      new_adjustments = (new (ggc_alloc <ipa_param_adjustments> ())
			 ipa_param_adjustments (new_params, count, false));
    }
  else
    new_adjustments = NULL;

  replace_trees = vec_safe_copy (node->clone.tree_map);
  for (i = 0; i < count; i++)
    {
      tree t = known_csts[i];
      if (t)
	{
	  struct ipa_replace_map *replace_map;

	  gcc_checking_assert (TREE_CODE (t) != TREE_BINFO);
	  replace_map = get_replacement_map (info, t, i);
	  if (replace_map)
	    vec_safe_push (replace_trees, replace_map);
	}
    }
  auto_vec<cgraph_edge *, 2> self_recursive_calls;
  for (i = callers.length () - 1; i >= 0; i--)
    {
      cgraph_edge *cs = callers[i];
      if (cs->caller == node)
	{
	  self_recursive_calls.safe_push (cs);
	  callers.unordered_remove (i);
	}
    }

  unsigned &suffix_counter = clone_num_suffixes->get_or_insert (
			       IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (
				 node->decl)));
  new_node = node->create_virtual_clone (callers, replace_trees,
					 new_adjustments, "constprop",
					 suffix_counter);
  suffix_counter++;

  bool have_self_recursive_calls = !self_recursive_calls.is_empty ();
  for (unsigned j = 0; j < self_recursive_calls.length (); j++)
    {
      cgraph_edge *cs = get_next_cgraph_edge_clone (self_recursive_calls[j]);
      /* Cloned edges can disappear during cloning as speculation can be
	 resolved, check that we have one and that it comes from the last
	 cloning.  */
      if (cs && cs->caller == new_node)
	cs->redirect_callee_duplicating_thunks (new_node);
      /* Any future code that would make more than one clone of an outgoing
	 edge would confuse this mechanism, so let's check that does not
	 happen.  */
      gcc_checking_assert (!cs
			   || !get_next_cgraph_edge_clone (cs)
			   || get_next_cgraph_edge_clone (cs)->caller != new_node);
    }
  if (have_self_recursive_calls)
    new_node->expand_all_artificial_thunks ();

  ipa_set_node_agg_value_chain (new_node, aggvals);
  for (av = aggvals; av; av = av->next)
    new_node->maybe_create_reference (av->value, NULL);

  if (dump_file && (dump_flags & TDF_DETAILS))
    {
      fprintf (dump_file, "     the new node is %s.\n", new_node->dump_name ());
      if (known_contexts.exists ())
	{
	  for (i = 0; i < count; i++)
	    if (!known_contexts[i].useless_p ())
	      {
		fprintf (dump_file, "     known ctx %i is ", i);
		known_contexts[i].dump (dump_file);
	      }
	}
      if (aggvals)
	ipa_dump_agg_replacement_values (dump_file, aggvals);
    }
  ipa_check_create_node_params ();
  update_profiling_info (node, new_node);
  new_info = IPA_NODE_REF (new_node);
  new_info->ipcp_orig_node = node;
  new_node->ipcp_clone = true;
  new_info->known_csts = known_csts;
  new_info->known_contexts = known_contexts;

  ipcp_discover_new_direct_edges (new_node, known_csts, known_contexts, aggvals);

  callers.release ();
  return new_node;
}

Project: gcc

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)
    cs = cs->next_caller;
  if (cs && IPA_NODE_REF (cs->caller))
    {
      IPA_NODE_REF (cs->caller)->node_calling_single_call = true;
      return true;
    }
  return false;
}

Project: gcc

safe_add (int a, int b)
{
  if (a > INT_MAX/2 || b > INT_MAX/2)
    return a > b ? a : b;
  else
    return a + b;
}

Project: gcc

find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
					    vec<tree> known_csts,
					    vec<cgraph_edge *> callers)
{
  class ipa_node_params *info = IPA_NODE_REF (node);
  int i, count = ipa_get_param_count (info);

  for (i = 0; i < count; i++)
    {
      struct cgraph_edge *cs;
      tree newval = NULL_TREE;
      int j;
      bool first = true;
      tree type = ipa_get_type (info, i);

      if (ipa_get_scalar_lat (info, i)->bottom || known_csts[i])
	continue;

      FOR_EACH_VEC_ELT (callers, j, cs)
	{
	  struct ipa_jump_func *jump_func;
	  tree t;

	  if (IPA_NODE_REF (cs->caller) && IPA_NODE_REF (cs->caller)->node_dead)
	    continue;

	  if (!IPA_EDGE_REF (cs)
	      || i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs))
	      || (i == 0
		  && call_passes_through_thunk_p (cs)))
	    {
	      newval = NULL_TREE;
	      break;
	    }
	  jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
	  if (self_recursive_pass_through_p (cs, jump_func, i))
	    continue;

	  t = ipa_value_from_jfunc (IPA_NODE_REF (cs->caller), jump_func, type);
	  if (!t
	      || (newval
		  && !values_equal_for_ipcp_p (t, newval))
	      || (!first && !newval))
	    {
	      newval = NULL_TREE;
	      break;
	    }
	  else
	    newval = t;
	  first = false;
	}

      if (newval)
	{
	  if (dump_file && (dump_flags & TDF_DETAILS))
	    {
	      fprintf (dump_file, "    adding an extra known scalar value ");
	      print_ipcp_constant_value (dump_file, newval);
	      fprintf (dump_file, " for ");
	      ipa_dump_param (dump_file, info, i);
	      fprintf (dump_file, "\n");
	    }

	  known_csts[i] = newval;
	}
    }
}

Project: gcc

  pass_ipa_cp (gcc::context *ctxt)
    : ipa_opt_pass_d (pass_data_ipa_cp, ctxt,
		      ipcp_generate_summary, /* generate_summary */
		      ipcp_write_summary, /* write_summary */
		      ipcp_read_summary, /* read_summary */
		      ipcp_write_transformation_summaries, /*
		      write_optimization_summary */
		      ipcp_read_transformation_summaries, /*
		      read_optimization_summary */
		      NULL, /* stmt_fixup */
		      0, /* function_transform_todo_flags_start */
		      ipcp_transform_function, /* function_transform */
		      NULL) /* variable_transform */
  {}

Project: gcc

ipcp_lattice<valtype>::add_value (valtype newval, cgraph_edge *cs,
				  ipcp_value<valtype> *src_val,
				  int src_idx, HOST_WIDE_INT offset,
				  ipcp_value<valtype> **val_p,
				  bool unlimited)
{
  ipcp_value<valtype> *val, *last_val = NULL;

  if (val_p)
    *val_p = NULL;

  if (bottom)
    return false;

  for (val = values; val; last_val = val, val = val->next)
    if (values_equal_for_ipcp_p (val->value, newval))
      {
	if (val_p)
	  *val_p = val;

	if (ipa_edge_within_scc (cs))
	  {
	    ipcp_value_source<valtype> *s;
	    for (s = val->sources; s; s = s->next)
	      if (s->cs == cs)
		break;
	    if (s)
	      return false;
	  }

	val->add_source (cs, src_val, src_idx, offset);
	return false;
      }

  if (!unlimited && values_count == param_ipa_cp_value_list_size)
    {
      /* We can only free sources, not the values themselves, because sources
	 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;
	      ipcp_sources_pool.remove ((ipcp_value_source<tree>*)src);
	    }
	}
      values = NULL;
      return set_to_bottom ();
    }

  values_count++;
  val = allocate_and_init_ipcp_value (newval);
  val->add_source (cs, src_val, src_idx, offset);
  val->next = NULL;

  /* Add the new value to end of value list, which can reduce iterations
     of propagation stage for recursive function.  */
  if (last_val)
    last_val->next = val;
  else
    values = val;

  if (val_p)
    *val_p = val;

  return true;
}

Project: gcc

propagate_aggs_across_jump_function (struct cgraph_edge *cs,
				     struct ipa_jump_func *jfunc,
				     class ipcp_param_lattices *dest_plats)
{
  bool ret = false;

  if (dest_plats->aggs_bottom)
    return false;

  if (jfunc->type == IPA_JF_PASS_THROUGH
      && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
    {
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      int src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
      class ipcp_param_lattices *src_plats;

      src_plats = ipa_get_parm_lattices (caller_info, src_idx);
      if (agg_pass_through_permissible_p (src_plats, jfunc))
	{
	  /* Currently we do not produce clobber aggregate jump
	     functions, replace with merging when we do.  */
	  gcc_assert (!jfunc->agg.items);
	  ret |= merge_aggregate_lattices (cs, dest_plats, src_plats,
					   src_idx, 0);
	}
      else
	ret |= set_agg_lats_contain_variable (dest_plats);
    }
  else if (jfunc->type == IPA_JF_ANCESTOR
	   && ipa_get_jf_ancestor_agg_preserved (jfunc))
    {
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      int src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
      class ipcp_param_lattices *src_plats;

      src_plats = ipa_get_parm_lattices (caller_info, src_idx);
      if (src_plats->aggs && src_plats->aggs_by_ref)
	{
	  /* Currently we do not produce clobber aggregate jump
	     functions, replace with merging when we do.  */
	  gcc_assert (!jfunc->agg.items);
	  ret |= merge_aggregate_lattices (cs, dest_plats, src_plats, src_idx,
					   ipa_get_jf_ancestor_offset (jfunc));
	}
      else if (!src_plats->aggs_by_ref)
	ret |= set_agg_lats_to_bottom (dest_plats);
      else
	ret |= set_agg_lats_contain_variable (dest_plats);
    }
  else if (jfunc->agg.items)
    {
      bool pre_existing = dest_plats->aggs != NULL;
      struct ipcp_agg_lattice **aglat = &dest_plats->aggs;
      struct ipa_agg_jf_item *item;
      int i;

      if (set_check_aggs_by_ref (dest_plats, jfunc->agg.by_ref))
	return true;

      FOR_EACH_VEC_ELT (*jfunc->agg.items, i, item)
	{
	  HOST_WIDE_INT val_size;

	  if (item->offset < 0 || item->jftype == IPA_JF_UNKNOWN)
	    continue;
	  val_size = tree_to_shwi (TYPE_SIZE (item->type));

	  if (merge_agg_lats_step (dest_plats, item->offset, val_size,
				   &aglat, pre_existing, &ret))
	    {
	      ret |= propagate_aggregate_lattice (cs, item, *aglat);
	      aglat = &(*aglat)->next;
	    }
	  else if (dest_plats->aggs_bottom)
	    return true;
	}

      ret |= set_chain_of_aglats_contains_variable (*aglat);
    }
  else
    ret |= set_agg_lats_contain_variable (dest_plats);

  return ret;
}

Project: gcc

add_all_node_vals_to_toposort (cgraph_node *node, ipa_topo_info *topo)
{
  class ipa_node_params *info = IPA_NODE_REF (node);
  int i, count = ipa_get_param_count (info);

  for (i = 0; i < count; i++)
    {
      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
      ipcp_lattice<tree> *lat = &plats->itself;
      struct ipcp_agg_lattice *aglat;

      if (!lat->bottom)
	{
	  ipcp_value<tree> *val;
	  for (val = lat->values; val; val = val->next)
	    topo->constants.add_val (val);
	}

      if (!plats->aggs_bottom)
	for (aglat = plats->aggs; aglat; aglat = aglat->next)
	  if (!aglat->bottom)
	    {
	      ipcp_value<tree> *val;
	      for (val = aglat->values; val; val = val->next)
		topo->constants.add_val (val);
	    }

      ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
      if (!ctxlat->bottom)
	{
	  ipcp_value<ipa_polymorphic_call_context> *ctxval;
	  for (ctxval = ctxlat->values; ctxval; ctxval = ctxval->next)
	    topo->contexts.add_val (ctxval);
	}
    }
}

Project: gcc

propagate_aggregate_lattice (struct cgraph_edge *cs,
			     struct ipa_agg_jf_item *item,
			     struct ipcp_agg_lattice *aglat)
{
  class ipa_node_params *caller_info;
  class ipcp_param_lattices *src_plats;
  struct ipcp_lattice<tree> *src_lat;
  HOST_WIDE_INT src_offset;
  int src_idx;
  tree load_type;
  bool ret;

  if (item->jftype == IPA_JF_CONST)
    {
      tree value = item->value.constant;

      gcc_checking_assert (is_gimple_ip_invariant (value));
      return aglat->add_value (value, cs, NULL, 0);
    }

  gcc_checking_assert (item->jftype == IPA_JF_PASS_THROUGH
		       || item->jftype == IPA_JF_LOAD_AGG);

  caller_info = IPA_NODE_REF (cs->caller);
  src_idx = item->value.pass_through.formal_id;
  src_plats = ipa_get_parm_lattices (caller_info, src_idx);

  if (item->jftype == IPA_JF_PASS_THROUGH)
    {
      load_type = NULL_TREE;
      src_lat = &src_plats->itself;
      src_offset = -1;
    }
  else
    {
      HOST_WIDE_INT load_offset = item->value.load_agg.offset;
      struct ipcp_agg_lattice *src_aglat;

      for (src_aglat = src_plats->aggs; src_aglat; src_aglat = src_aglat->next)
	if (src_aglat->offset >= load_offset)
	  break;

      load_type = item->value.load_agg.type;
      if (!src_aglat
	  || src_aglat->offset > load_offset
	  || src_aglat->size != tree_to_shwi (TYPE_SIZE (load_type))
	  || src_plats->aggs_by_ref != item->value.load_agg.by_ref)
	return aglat->set_contains_variable ();

      src_lat = src_aglat;
      src_offset = load_offset;
    }

  if (src_lat->bottom
      || (!ipcp_versionable_function_p (cs->caller)
	  && !src_lat->is_single_const ()))
    return aglat->set_contains_variable ();

  ret = propagate_vals_across_arith_jfunc (cs,
					   item->value.pass_through.operation,
					   load_type,
					   item->value.pass_through.operand,
					   src_lat, aglat,
					   src_offset,
					   src_idx,
					   item->type);

  if (src_lat->contains_variable)
    ret |= aglat->set_contains_variable ();

  return ret;
}

Project: gcc

determine_versionability (struct cgraph_node *node,
			  class ipa_node_params *info)
{
  const char *reason = NULL;

  /* There are a number of generic reasons functions cannot be versioned.  We
     also cannot remove parameters if there are type attributes such as fnspec
     present.  */
  if (node->alias || node->thunk.thunk_p)
    reason = "alias or thunk";
  else if (!node->versionable)
    reason = "not a tree_versionable_function";
  else if (node->get_availability () <= AVAIL_INTERPOSABLE)
    reason = "insufficient body availability";
  else if (!opt_for_fn (node->decl, optimize)
	   || !opt_for_fn (node->decl, flag_ipa_cp))
    reason = "non-optimized function";
  else if (lookup_attribute ("omp declare simd", DECL_ATTRIBUTES (node->decl)))
    {
      /* Ideally we should clone the SIMD clones themselves and create
	 vector copies of them, so IPA-cp and SIMD clones can happily
	 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";
    }

  /* Functions calling BUILT_IN_VA_ARG_PACK and BUILT_IN_VA_ARG_PACK_LEN
     work only when inlined.  Cloning them may still lead to better code
     because ipa-cp will not give up on cloning further.  If the function is
     external this however leads to wrong code because we may end up producing
     offline copy of the function.  */
  if (DECL_EXTERNAL (node->decl))
    for (cgraph_edge *edge = node->callees; !reason && edge;
	 edge = edge->next_callee)
      if (fndecl_built_in_p (edge->callee->decl, BUILT_IN_NORMAL))
        {
	  if (DECL_FUNCTION_CODE (edge->callee->decl) == BUILT_IN_VA_ARG_PACK)
	    reason = "external function which calls va_arg_pack";
	  if (DECL_FUNCTION_CODE (edge->callee->decl)
	      == BUILT_IN_VA_ARG_PACK_LEN)
	    reason = "external function which calls va_arg_pack_len";
        }

  if (reason && dump_file && !node->alias && !node->thunk.thunk_p)
    fprintf (dump_file, "Function %s is not versionable, reason: %s.\n",
	     node->dump_name (), reason);

  info->versionable = (reason == NULL);
}

Project: gcc

ipcp_decision_stage (class ipa_topo_info *topo)
{
  int i;

  if (dump_file)
    fprintf (dump_file, "\nIPA decision stage:\n\n");

  for (i = topo->nnodes - 1; i >= 0; i--)
    {
      struct cgraph_node *node = topo->order[i];
      bool change = false, iterate = true;

      while (iterate)
	{
	  struct cgraph_node *v;
	  iterate = false;
	  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);

	  change |= iterate;
	}
      if (change)
	identify_dead_nodes (node);
    }
}

Project: gcc

ipcp_vr_lattice::print (FILE * f)
{
  dump_value_range (f, &m_vr);
}

Project: gcc

build_toporder_info (class ipa_topo_info *topo)
{
  topo->order = XCNEWVEC (struct cgraph_node *, symtab->cgraph_count);
  topo->stack = XCNEWVEC (struct cgraph_node *, symtab->cgraph_count);

  gcc_checking_assert (topo->stack_top == 0);
  topo->nnodes = ipa_reduced_postorder (topo->order, true,
					ignore_edge_p);
}

Project: gcc

ipcp_vr_lattice::meet_with (const value_range *p_vr)
{
  return meet_with_1 (p_vr);
}

Project: gcc

ipa_get_jf_arith_result (enum tree_code opcode, tree input, tree operand,
			 tree res_type)
{
  tree res;

  if (opcode == NOP_EXPR)
    return input;
  if (!is_gimple_ip_invariant (input))
    return NULL_TREE;

  if (!res_type)
    {
      if (TREE_CODE_CLASS (opcode) == tcc_comparison)
	res_type = boolean_type_node;
      else if (expr_type_first_operand_type_p (opcode))
	res_type = TREE_TYPE (input);
      else
	return NULL_TREE;
    }

  if (TREE_CODE_CLASS (opcode) == tcc_unary)
    res = fold_unary (opcode, res_type, input);
  else
    res = fold_binary (opcode, res_type, input, operand);

  if (res && !is_gimple_ip_invariant (res))
    return NULL_TREE;

  return res;
}

Project: gcc

get_clone_agg_value (struct cgraph_node *node, HOST_WIDE_INT offset,
		     int index)
{
  struct ipa_agg_replacement_value *aggval;

  aggval = ipa_get_agg_replacements_for_node (node);
  while (aggval)
    {
      if (aggval->offset == offset
	  && aggval->index == index)
	return aggval->value;
      aggval = aggval->next;
    }
  return NULL_TREE;
}

Project: gcc

context_independent_aggregate_values (class ipcp_param_lattices *plats)
{
  vec<ipa_agg_value> res = vNULL;

  if (plats->aggs_bottom
      || plats->aggs_contain_variable
      || plats->aggs_count == 0)
    return vNULL;

  for (struct ipcp_agg_lattice *aglat = plats->aggs;
       aglat;
       aglat = aglat->next)
    if (aglat->is_single_const ())
      {
	struct ipa_agg_value item;
	item.offset = aglat->offset;
	item.value = aglat->values->value;
	res.safe_push (item);
      }
  return res;
}

Project: gcc

intersect_with_plats (class ipcp_param_lattices *plats,
		      vec<ipa_agg_value> *inter,
		      HOST_WIDE_INT offset)
{
  struct ipcp_agg_lattice *aglat;
  struct ipa_agg_value *item;
  int k;

  if (!plats->aggs || plats->aggs_contain_variable || plats->aggs_bottom)
    {
      inter->release ();
      return;
    }

  aglat = plats->aggs;
  FOR_EACH_VEC_ELT (*inter, k, item)
    {
      bool found = false;
      if (!item->value)
	continue;
      while (aglat)
	{
	  if (aglat->offset - offset > item->offset)
	    break;
	  if (aglat->offset - offset == item->offset)
	    {
	      gcc_checking_assert (item->value);
	      if (aglat->is_single_const ())
		{
		  tree value = aglat->values->value;

		  if (values_equal_for_ipcp_p (item->value, value))
		    found = true;
		  else if (item->value == error_mark_node)
		    {
		      /* Replace unknown place holder value with real one.  */
		      item->value = value;
		      found = true;
		    }
		}
	      break;
	    }
	  aglat = aglat->next;
	}
      if (!found)
	item->value = NULL_TREE;
    }
}

Project: gcc

ipcp_cloning_candidate_p (struct cgraph_node *node)
{
  struct caller_statistics stats;

  gcc_checking_assert (node->has_gimple_body_p ());

  if (!opt_for_fn (node->decl, flag_ipa_cp_clone))
    {
      if (dump_file)
	fprintf (dump_file, "Not considering %s for cloning; "
		 "-fipa-cp-clone disabled.\n",
 		 node->name ());
      return false;
    }

  if (node->optimize_for_size_p ())
    {
      if (dump_file)
	fprintf (dump_file, "Not considering %s for cloning; "
		 "optimizing it for size.\n",
 		 node->name ());
      return false;
    }

  init_caller_stats (&stats);
  node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats, false);

  if (ipa_size_summaries->get (node)->self_size < stats.n_calls)
    {
      if (dump_file)
	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 > profile_count::zero ())
    {
      if (stats.count_sum > node->count.ipa ().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)
    {
      if (dump_file)
	fprintf (dump_file, "Not considering %s for cloning; no hot calls.\n",
		 node->name ());
      return false;
    }
  if (dump_file)
    fprintf (dump_file, "Considering %s for cloning.\n",
	     node->name ());
  return true;
}

Project: gcc

int main() {

}

Project: gcc

ipcp_discover_new_direct_edges (struct cgraph_node *node,
				vec<tree> known_csts,
				vec<ipa_polymorphic_call_context>
				known_contexts,
				struct ipa_agg_replacement_value *aggvals)
{
  struct cgraph_edge *ie, *next_ie;
  bool found = false;

  for (ie = node->indirect_calls; ie; ie = next_ie)
    {
      tree target;
      bool speculative;

      next_ie = ie->next_callee;
      target = ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts,
					       vNULL, aggvals, &speculative);
      if (target)
	{
	  bool agg_contents = ie->indirect_info->agg_contents;
	  bool polymorphic = ie->indirect_info->polymorphic;
	  int param_index = ie->indirect_info->param_index;
	  struct cgraph_edge *cs = ipa_make_edge_direct_to_target (ie, target,
								   speculative);
	  found = true;

	  if (cs && !agg_contents && !polymorphic)
	    {
	      class ipa_node_params *info = IPA_NODE_REF (node);
	      int c = ipa_get_controlled_uses (info, param_index);
	      if (c != IPA_UNDESCRIBED_USE)
		{
		  struct ipa_ref *to_del;

		  c--;
		  ipa_set_controlled_uses (info, param_index, c);
		  if (dump_file && (dump_flags & TDF_DETAILS))
		    fprintf (dump_file, "     controlled uses count of param "
			     "%i bumped down to %i\n", param_index, c);
		  if (c == 0
		      && (to_del = node->find_reference (cs->callee, NULL, 0)))
		    {
		      if (dump_file && (dump_flags & TDF_DETAILS))
			fprintf (dump_file, "       and even removing its "
				 "cloning-created reference\n");
		      to_del->remove_reference ();
		    }
		}
	    }
	}
    }
  /* Turning calls to direct calls will improve overall summary.  */
  if (found)
    ipa_update_overall_fn_summary (node);
}

Project: gcc

want_remove_some_param_p (cgraph_node *node, vec<tree> known_csts)
{
  auto_vec<bool, 16> surviving;
  bool filled_vec = false;
  ipa_node_params *info = IPA_NODE_REF (node);
  int i, count = ipa_get_param_count (info);

  for (i = 0; i < count; i++)
    {
      if (!known_csts[i] && ipa_is_param_used (info, i))
       continue;

      if (!filled_vec)
       {
         if (!node->clone.param_adjustments)
           return true;
         node->clone.param_adjustments->get_surviving_params (&surviving);
         filled_vec = true;
       }
      if (surviving.length() < (unsigned) i &&  surviving[i])
       return true;
    }
  return false;
}

Project: gcc

perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
			       vec<ipa_polymorphic_call_context> known_contexts,
			       vec<ipa_agg_value_set> known_aggs,
			       int removable_params_cost,
			       int est_move_cost, ipcp_value_base *val)
{
  int size, time_benefit;
  sreal time, base_time;
  ipa_hints hints;

  estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
				     known_aggs, &size, &time,
				     &base_time, &hints);
  base_time -= time;
  if (base_time > 65535)
    base_time = 65535;

  /* Extern inline functions have no cloning local time benefits because they
     will be inlined anyway.  The only reason to clone them is if it enables
     optimization in any of the functions they call.  */
  if (DECL_EXTERNAL (node->decl) && DECL_DECLARED_INLINE_P (node->decl))
    time_benefit = 0;
  else
    time_benefit = base_time.to_int ()
      + devirtualization_time_bonus (node, known_csts, known_contexts,
				     known_aggs)
      + hint_time_bonus (hints)
      + removable_params_cost + est_move_cost;

  gcc_checking_assert (size >=0);
  /* The inliner-heuristics based estimates may think that in certain
     contexts some functions do not have any size at all but we want
     all specializations to have at least a tiny cost, not least not to
     divide by zero.  */
  if (size == 0)
    size = 1;

  val->local_time_benefit = time_benefit;
  val->local_size_cost = size;
}

Project: gcc

ipa_value_from_jfunc (class ipa_node_params *info, struct ipa_jump_func *jfunc,
		      tree parm_type)
{
  if (jfunc->type == IPA_JF_CONST)
    return ipa_get_jf_constant (jfunc);
  else if (jfunc->type == IPA_JF_PASS_THROUGH
	   || jfunc->type == IPA_JF_ANCESTOR)
    {
      tree input;
      int idx;

      if (jfunc->type == IPA_JF_PASS_THROUGH)
	idx = ipa_get_jf_pass_through_formal_id (jfunc);
      else
	idx = ipa_get_jf_ancestor_formal_id (jfunc);

      if (info->ipcp_orig_node)
	input = info->known_csts[idx];
      else
	{
	  ipcp_lattice<tree> *lat;

	  if (!info->lattices
	      || idx >= ipa_get_param_count (info))
	    return NULL_TREE;
	  lat = ipa_get_scalar_lat (info, idx);
	  if (!lat->is_single_const ())
	    return NULL_TREE;
	  input = lat->values->value;
	}

      if (!input)
	return NULL_TREE;

      if (jfunc->type == IPA_JF_PASS_THROUGH)
	return ipa_get_jf_pass_through_result (jfunc, input, parm_type);
      else
	return ipa_get_jf_ancestor_result (jfunc, input);
    }
  else
    return NULL_TREE;
}

Project: gcc

ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input,
				tree res_type)
{
  return ipa_get_jf_arith_result (ipa_get_jf_pass_through_operation (jfunc),
				  input,
				  ipa_get_jf_pass_through_operand (jfunc),
				  res_type);
}

Project: gcc

ipcp_lattice<valtype>::set_to_bottom ()
{
  bool ret = !bottom;
  bottom = true;
  return ret;
}

Project: gcc

cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs,
					  struct cgraph_node *node)
{
  class ipa_node_params *orig_node_info;
  struct ipa_agg_replacement_value *aggval;
  int i, ec, count;

  aggval = ipa_get_agg_replacements_for_node (node);
  if (!aggval)
    return true;

  count = ipa_get_param_count (IPA_NODE_REF (node));
  ec = ipa_get_cs_argument_count (IPA_EDGE_REF (cs));
  if (ec < count)
    for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next)
      if (aggval->index >= ec)
	return false;

  orig_node_info = IPA_NODE_REF (IPA_NODE_REF (node)->ipcp_orig_node);

  for (i = 0; i < count; i++)
    {
      class ipcp_param_lattices *plats;
      bool interesting = false;
      for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next)
	if (aggval->index == i)
	  {
	    interesting = true;
	    break;
	  }
      if (!interesting)
	continue;

      plats = ipa_get_parm_lattices (orig_node_info, aggval->index);
      if (plats->aggs_bottom)
	return false;

      vec<ipa_agg_value> values = intersect_aggregates_with_edge (cs, i, vNULL);
      if (!values.exists ())
	return false;

      for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next)
	if (aggval->index == i)
	  {
	    struct ipa_agg_value *item;
	    int j;
	    bool found = false;
	    FOR_EACH_VEC_ELT (values, j, item)
	      if (item->value
		  && item->offset == av->offset
		  && values_equal_for_ipcp_p (item->value, av->value))
		{
		  found = true;
		  break;
		}
	    if (!found)
	      {
		values.release ();
		return false;
	      }
	  }
      values.release ();
    }
  return true;
}

Project: gcc

agg_replacements_to_vector (struct cgraph_node *node, int index,
			    HOST_WIDE_INT offset)
{
  struct ipa_agg_replacement_value *av;
  vec<ipa_agg_value> res = vNULL;

  for (av = ipa_get_agg_replacements_for_node (node); av; av = av->next)
    if (av->index == index
	&& (av->offset - offset) >= 0)
    {
      struct ipa_agg_value item;
      gcc_checking_assert (av->value);
      item.offset = av->offset - offset;
      item.value = av->value;
      res.safe_push (item);
    }

  return res;
}

Project: gcc

cgraph_edge_brings_all_scalars_for_node (struct cgraph_edge *cs,
					 struct cgraph_node *node)
{
  class ipa_node_params *dest_info = IPA_NODE_REF (node);
  int count = ipa_get_param_count (dest_info);
  class ipa_node_params *caller_info;
  class ipa_edge_args *args;
  int i;

  caller_info = IPA_NODE_REF (cs->caller);
  args = IPA_EDGE_REF (cs);
  for (i = 0; i < count; i++)
    {
      struct ipa_jump_func *jump_func;
      tree val, t;

      val = dest_info->known_csts[i];
      if (!val)
	continue;

      if (i >= ipa_get_cs_argument_count (args))
	return false;
      jump_func = ipa_get_ith_jump_func (args, i);
      t = ipa_value_from_jfunc (caller_info, jump_func,
				ipa_get_type (dest_info, i));
      if (!t || !values_equal_for_ipcp_p (val, t))
	return false;
    }
  return true;
}

Project: gcc

  value_topo_info () : values_topo (NULL), stack (NULL), dfs_counter (0)
  {}

Project: gcc

  ipcp_value_base ()
    : local_time_benefit (0), local_size_cost (0),
      prop_time_benefit (0), prop_size_cost (0) {}

Project: gcc

ipa_get_scalar_lat (class ipa_node_params *info, int i)
{
  class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
  return &plats->itself;
}

Project: gcc

set_check_aggs_by_ref (class ipcp_param_lattices *dest_plats,
		       bool new_aggs_by_ref)
{
  if (dest_plats->aggs)
    {
      if (dest_plats->aggs_by_ref != new_aggs_by_ref)
	{
	  set_agg_lats_to_bottom (dest_plats);
	  return true;
	}
    }
  else
    dest_plats->aggs_by_ref = new_aggs_by_ref;
  return false;
}

Project: gcc

hint_time_bonus (ipa_hints hints)
{
  int result = 0;
  if (hints & (INLINE_HINT_loop_iterations | INLINE_HINT_loop_stride))
    result += param_ipa_cp_loop_hint_bonus;
  return result;
}

Project: gcc

values_equal_for_ipcp_p (tree x, tree y)
{
  gcc_checking_assert (x != NULL_TREE && y != NULL_TREE);

  if (x == y)
    return true;

  if (TREE_CODE (x) ==  ADDR_EXPR
      && TREE_CODE (y) ==  ADDR_EXPR
      && TREE_CODE (TREE_OPERAND (x, 0)) == CONST_DECL
      && TREE_CODE (TREE_OPERAND (y, 0)) == CONST_DECL)
    return operand_equal_p (DECL_INITIAL (TREE_OPERAND (x, 0)),
			    DECL_INITIAL (TREE_OPERAND (y, 0)), 0);
  else
    return operand_equal_p (x, y, 0);
}

Project: gcc

ipa_get_jf_ancestor_result (struct ipa_jump_func *jfunc, tree input)
{
  gcc_checking_assert (TREE_CODE (input) != TREE_BINFO);
  if (TREE_CODE (input) == ADDR_EXPR)
    {
      tree t = TREE_OPERAND (input, 0);
      t = build_ref_for_offset (EXPR_LOCATION (t), t,
				ipa_get_jf_ancestor_offset (jfunc), false,
				ptr_type_node, NULL, false);
      return build_fold_addr_expr (t);
    }
  else
    return NULL_TREE;
}

Project: gcc

  edge_clone_summary (): prev_clone (NULL), next_clone (NULL) {}

Project: gcc

ipa_cp_c_finalize (void)
{
  max_count = profile_count::uninitialized ();
  overall_size = 0;
  max_new_size = 0;
  ipcp_free_transformation_sum ();
}

Project: gcc

propagate_bits_across_jump_function (cgraph_edge *cs, int idx,
				     ipa_jump_func *jfunc,
				     ipcp_bits_lattice *dest_lattice)
{
  if (dest_lattice->bottom_p ())
    return false;

  enum availability availability;
  cgraph_node *callee = cs->callee->function_symbol (&availability);
  class 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.  Similarly, we can have bad type mismatches
     with LTO, avoid doing anything with those too.  */
  if (!parm_type
      || (!INTEGRAL_TYPE_P (parm_type) && !POINTER_TYPE_P (parm_type)))
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
	fprintf (dump_file, "Setting dest_lattice to bottom, because type of "
		 "param %i of %s is NULL or unsuitable for bits propagation\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)
    {
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      tree operand = NULL_TREE;
      enum tree_code code;
      unsigned src_idx;

      if (jfunc->type == IPA_JF_PASS_THROUGH)
	{
	  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);
	  unsigned HOST_WIDE_INT offset = ipa_get_jf_ancestor_offset (jfunc) / BITS_PER_UNIT;
	  operand = build_int_cstu (size_type_node, offset);
	}

      class ipcp_param_lattices *src_lats
	= ipa_get_parm_lattices (caller_info, src_idx);

      /* 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);
    }

  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 ();
}

Project: gcc

agg_pass_through_permissible_p (class ipcp_param_lattices *src_plats,
				struct ipa_jump_func *jfunc)
{
  return src_plats->aggs
    && (!src_plats->aggs_by_ref
	|| ipa_get_jf_pass_through_agg_preserved (jfunc));
}

Project: gcc

  bool constant_p () { return m_lattice_val == IPA_BITS_CONSTANT; }

Project: gcc

ipcp_lattice<valtype>::print (FILE * f, bool dump_sources, bool dump_benefits)
{
  ipcp_value<valtype> *val;
  bool prev = false;

  if (bottom)
    {
      fprintf (f, "BOTTOM\n");
      return;
    }

  if (!values_count && !contains_variable)
    {
      fprintf (f, "TOP\n");
      return;
    }

  if (contains_variable)
    {
      fprintf (f, "VARIABLE");
      prev = true;
      if (dump_benefits)
	fprintf (f, "\n");
    }

  for (val = values; val; val = val->next)
    {
      if (dump_benefits && prev)
	fprintf (f, "               ");
      else if (!dump_benefits && prev)
	fprintf (f, ", ");
      else
	prev = true;

      print_ipcp_constant_value (f, val->value);

      if (dump_sources)
	{
	  ipcp_value_source<valtype> *s;

	  fprintf (f, " [from:");
	  for (s = val->sources; s; s = s->next)
	    fprintf (f, " %i(%f)", s->cs->caller->order,
		     s->cs->sreal_frequency ().to_double ());
	  fprintf (f, "]");
	}

      if (dump_benefits)
	fprintf (f, " [loc_time: %i, loc_size: %i, "
		 "prop_time: %i, prop_size: %i]\n",
		 val->local_time_benefit, val->local_size_cost,
		 val->prop_time_benefit, val->prop_size_cost);
    }
  if (!dump_benefits)
    fprintf (f, "\n");
}

Project: gcc

ipa_get_parm_lattices (class ipa_node_params *info, int i)
{
  gcc_assert (i >= 0 && i < ipa_get_param_count (info));
  gcc_checking_assert (!info->ipcp_orig_node);
  gcc_checking_assert (info->lattices);
  return &(info->lattices[i]);
}

Project: gcc

  ipa_topo_info () : order(NULL), stack(NULL), nnodes(0), stack_top(0),
    constants ()
  {}

Project: gcc

edge_clone_summary_t::duplicate (cgraph_edge *src_edge, cgraph_edge *dst_edge,
				 edge_clone_summary *src_data,
				 edge_clone_summary *dst_data)
{
  if (src_data->next_clone)
    edge_clone_summaries->get (src_data->next_clone)->prev_clone = dst_edge;
  dst_data->prev_clone = src_edge;
  dst_data->next_clone = src_data->next_clone;
  src_data->next_clone = dst_edge;
}

Project: gcc

print_all_lattices (FILE * f, bool dump_sources, bool dump_benefits)
{
  struct cgraph_node *node;
  int i, count;

  fprintf (f, "\nLattices:\n");
  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
    {
      class ipa_node_params *info;

      info = IPA_NODE_REF (node);
      /* Skip constprop clones since we don't make lattices for them.  */
      if (info->ipcp_orig_node)
	continue;
      fprintf (f, "  Node: %s:\n", node->dump_name ());
      count = ipa_get_param_count (info);
      for (i = 0; i < count; i++)
	{
	  struct ipcp_agg_lattice *aglat;
	  class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
	  fprintf (f, "    param [%d]: ", i);
	  plats->itself.print (f, dump_sources, dump_benefits);
	  fprintf (f, "         ctxs: ");
	  plats->ctxlat.print (f, dump_sources, dump_benefits);
	  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");

	  if (plats->aggs_bottom)
	    {
	      fprintf (f, "        AGGS BOTTOM\n");
	      continue;
	    }
	  if (plats->aggs_contain_variable)
	    fprintf (f, "        AGGS VARIABLE\n");
	  for (aglat = plats->aggs; aglat; aglat = aglat->next)
	    {
	      fprintf (f, "        %soffset " HOST_WIDE_INT_PRINT_DEC ": ",
		       plats->aggs_by_ref ? "ref " : "", aglat->offset);
	      aglat->print (f, dump_sources, dump_benefits);
	    }
	}
    }
}

Project: gcc

known_contexts_useful_p (vec<ipa_polymorphic_call_context> known_contexts)
{
  ipa_polymorphic_call_context *ctx;
  int i;

  FOR_EACH_VEC_ELT (known_contexts, i, ctx)
    if (!ctx->useless_p ())
      return true;
  return false;
}

Project: gcc

ipcp_vr_lattice::meet_with_1 (const value_range *other_vr)
{
  if (bottom_p ())
    return false;

  if (other_vr->varying_p ())
    return set_to_bottom ();

  value_range save (m_vr);
  m_vr.union_ (other_vr);
  return !m_vr.equal_p (save);
}

Project: gcc

self_recursively_generated_p (ipcp_value<tree> *val)
{
  class ipa_node_params *info = NULL;

  for (ipcp_value_source<tree> *src = val->sources; src; src = src->next)
    {
      cgraph_edge *cs = src->cs;

      if (!src->val || cs->caller != cs->callee->function_symbol ()
	  || src->val == val)
	return false;

      if (!info)
	info = IPA_NODE_REF (cs->caller);

      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info,
								src->index);
      ipcp_lattice<tree> *src_lat = src->offset == -1 ? &plats->itself
						      : plats->aggs;
      ipcp_value<tree> *src_val;

      for (src_val = src_lat->values; src_val; src_val = src_val->next)
	if (src_val == val)
	  break;

      if (!src_val)
	return false;
    }

  return true;
}

Project: gcc

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;
}

Project: gcc

ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx,
			ipa_jump_func *jfunc)
{
  ipa_edge_args *args = IPA_EDGE_REF (cs);
  ipa_polymorphic_call_context ctx;
  ipa_polymorphic_call_context *edge_ctx
    = cs ? ipa_get_ith_polymorhic_call_context (args, csidx) : NULL;

  if (edge_ctx && !edge_ctx->useless_p ())
    ctx = *edge_ctx;

  if (jfunc->type == IPA_JF_PASS_THROUGH
      || jfunc->type == IPA_JF_ANCESTOR)
    {
      ipa_polymorphic_call_context srcctx;
      int srcidx;
      bool type_preserved = true;
      if (jfunc->type == IPA_JF_PASS_THROUGH)
	{
	  if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR)
	    return ctx;
	  type_preserved = ipa_get_jf_pass_through_type_preserved (jfunc);
	  srcidx = ipa_get_jf_pass_through_formal_id (jfunc);
	}
      else
	{
	  type_preserved = ipa_get_jf_ancestor_type_preserved (jfunc);
	  srcidx = ipa_get_jf_ancestor_formal_id (jfunc);
	}
      if (info->ipcp_orig_node)
	{
	  if (info->known_contexts.exists ())
	    srcctx = info->known_contexts[srcidx];
	}
      else
	{
	  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 (!lat->is_single_const ())
	    return ctx;
	  srcctx = lat->values->value;
	}
      if (srcctx.useless_p ())
	return ctx;
      if (jfunc->type == IPA_JF_ANCESTOR)
	srcctx.offset_by (ipa_get_jf_ancestor_offset (jfunc));
      if (!type_preserved)
	srcctx.possible_dynamic_type_change (cs->in_polymorphic_cdtor);
      srcctx.combine_with (ctx);
      return srcctx;
    }

  return ctx;
}

Project: gcc

copy_plats_to_inter (class ipcp_param_lattices *plats, HOST_WIDE_INT offset)
{
  vec<ipa_agg_value> res = vNULL;

  if (!plats->aggs || plats->aggs_contain_variable || plats->aggs_bottom)
    return vNULL;

  for (struct ipcp_agg_lattice *aglat = plats->aggs; aglat; aglat = aglat->next)
    if (aglat->is_single_const ())
      {
	struct ipa_agg_value ti;
	ti.offset = aglat->offset - offset;
	ti.value = aglat->values->value;
	res.safe_push (ti);
      }
  return res;
}

Project: gcc

get_val_across_arith_op (enum tree_code opcode,
			 tree opnd1_type,
			 tree opnd2,
			 ipcp_value<tree> *src_val,
			 tree res_type)
{
  tree opnd1 = src_val->value;

  /* Skip source values that is incompatible with specified type.  */
  if (opnd1_type
      && !useless_type_conversion_p (opnd1_type, TREE_TYPE (opnd1)))
    return NULL_TREE;

  return ipa_get_jf_arith_result (opcode, opnd1, opnd2, res_type);
}

Project: gcc

initialize_node_lattices (struct cgraph_node *node)
{
  class ipa_node_params *info = IPA_NODE_REF (node);
  struct cgraph_edge *ie;
  bool disable = false, variable = false;
  int i;

  gcc_checking_assert (node->has_gimple_body_p ());

  if (!ipa_get_param_count (info))
    disable = true;
  else if (node->local)
    {
      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
	 later.  */
      if (ipcp_versionable_function_p (node)
	  && ipcp_cloning_candidate_p (node))
	variable = true;
      else
	disable = true;
    }

  if (dump_file && (dump_flags & TDF_DETAILS)
      && !node->alias && !node->thunk.thunk_p)
    {
      fprintf (dump_file, "Initializing lattices of %s\n",
	       node->dump_name ());
      if (disable || variable)
	fprintf (dump_file, "  Marking all lattices as %s\n",
		 disable ? "BOTTOM" : "VARIABLE");
    }

  auto_vec<bool, 16> surviving_params;
  bool pre_modified = false;
  if (!disable && node->clone.param_adjustments)
    {
      /* At the moment all IPA optimizations should use the number of
	 parameters of the prevailing decl as the m_always_copy_start.
	 Handling any other value would complicate the code below, so for the
	 time bing let's only assert it is so.  */
      gcc_assert ((node->clone.param_adjustments->m_always_copy_start
		   == ipa_get_param_count (info))
		  || node->clone.param_adjustments->m_always_copy_start < 0);

      pre_modified = true;
      node->clone.param_adjustments->get_surviving_params (&surviving_params);

      if (dump_file && (dump_flags & TDF_DETAILS)
	  && !node->alias && !node->thunk.thunk_p)
	{
	  bool first = true;
	  for (int j = 0; j < ipa_get_param_count (info); j++)
	    {
	      if (j < (int) surviving_params.length ()
		  && surviving_params[j])
		continue;
	      if (first)
		{
		  fprintf (dump_file,
			   "  The following parameters are dead on arrival:");
		  first = false;
		}
	      fprintf (dump_file, " %u", j);
	    }
	  if (!first)
	      fprintf (dump_file, "\n");
	}
    }

  for (i = 0; i < ipa_get_param_count (info); i++)
    {
      ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
      if (disable
	  || (pre_modified && (surviving_params.length () <= (unsigned) i
			       || !surviving_params[i])))
	{
	  plats->itself.set_to_bottom ();
	  plats->ctxlat.set_to_bottom ();
	  set_agg_lats_to_bottom (plats);
	  plats->bits_lattice.set_to_bottom ();
	  plats->m_value_range.set_to_bottom ();
	}
      else
	{
	  plats->m_value_range.init ();
	  if (variable)
	    set_all_contains_variable (plats);
	}
    }

  for (ie = node->indirect_calls; ie; ie = ie->next_callee)
    if (ie->indirect_info->polymorphic
	&& ie->indirect_info->param_index >= 0)
      {
	gcc_checking_assert (ie->indirect_info->param_index >= 0);
	ipa_get_parm_lattices (info,
			       ie->indirect_info->param_index)->virt_call = 1;
      }
}

Project: gcc

propagate_context_across_jump_function (cgraph_edge *cs,
			  ipa_jump_func *jfunc, int idx,
			  ipcp_lattice<ipa_polymorphic_call_context> *dest_lat)
{
  ipa_edge_args *args = IPA_EDGE_REF (cs);
  if (dest_lat->bottom)
    return false;
  bool ret = false;
  bool added_sth = false;
  bool type_preserved = true;

  ipa_polymorphic_call_context edge_ctx, *edge_ctx_ptr
    = ipa_get_ith_polymorhic_call_context (args, idx);

  if (edge_ctx_ptr)
    edge_ctx = *edge_ctx_ptr;

  if (jfunc->type == IPA_JF_PASS_THROUGH
      || jfunc->type == IPA_JF_ANCESTOR)
    {
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      int src_idx;
      ipcp_lattice<ipa_polymorphic_call_context> *src_lat;

      /* TODO: Once we figure out how to propagate speculations, it will
	 probably be a good idea to switch to speculation if type_preserved is
	 not set instead of punting.  */
      if (jfunc->type == IPA_JF_PASS_THROUGH)
	{
	  if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR)
	    goto prop_fail;
	  type_preserved = ipa_get_jf_pass_through_type_preserved (jfunc);
	  src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
	}
      else
	{
	  type_preserved = ipa_get_jf_ancestor_type_preserved (jfunc);
	  src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
	}

      src_lat = ipa_get_poly_ctx_lat (caller_info, src_idx);
      /* If we would need to clone the caller and cannot, do not propagate.  */
      if (!ipcp_versionable_function_p (cs->caller)
	  && (src_lat->contains_variable
	      || (src_lat->values_count > 1)))
	goto prop_fail;

      ipcp_value<ipa_polymorphic_call_context> *src_val;
      for (src_val = src_lat->values; src_val; src_val = src_val->next)
	{
	  ipa_polymorphic_call_context cur = src_val->value;

	  if (!type_preserved)
	    cur.possible_dynamic_type_change (cs->in_polymorphic_cdtor);
	  if (jfunc->type == IPA_JF_ANCESTOR)
	    cur.offset_by (ipa_get_jf_ancestor_offset (jfunc));
	  /* TODO: In cases we know how the context is going to be used,
	     we can improve the result by passing proper OTR_TYPE.  */
	  cur.combine_with (edge_ctx);
	  if (!cur.useless_p ())
	    {
	      if (src_lat->contains_variable
		  && !edge_ctx.equal_to (cur))
		ret |= dest_lat->set_contains_variable ();
	      ret |= dest_lat->add_value (cur, cs, src_val, src_idx);
	      added_sth = true;
	    }
	}
    }

 prop_fail:
  if (!added_sth)
    {
      if (!edge_ctx.useless_p ())
	ret |= dest_lat->add_value (edge_ctx, cs);
      else
	ret |= dest_lat->set_contains_variable ();
    }

  return ret;
}

Project: gcc

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);
}

Project: gcc

gather_edges_for_value (ipcp_value<valtype> *val, cgraph_node *dest,
			int caller_count)
{
  ipcp_value_source<valtype> *src;
  vec<cgraph_edge *> ret;

  ret.create (caller_count);
  for (src = val->sources; src; src = src->next)
    {
      struct cgraph_edge *cs = src->cs;
      while (cs)
	{
	  if (cgraph_edge_brings_value_p (cs, src, dest, val))
	    ret.quick_push (cs);
	  cs = get_next_cgraph_edge_clone (cs);
	}
    }

  return ret;
}

Project: gcc

ipcp_val_agg_replacement_ok_p (ipa_agg_replacement_value *aggvals,
			       int index, HOST_WIDE_INT offset, tree value)
{
  if (offset == -1)
    return true;

  while (aggvals)
    {
      if (aggvals->index == index
	  && aggvals->offset == offset
	  && values_equal_for_ipcp_p (aggvals->value, value))
	return true;
      aggvals = aggvals->next;
    }
  return false;
}

Project: gcc

value_topo_info<valtype>::propagate_effects ()
{
  ipcp_value<valtype> *base;

  for (base = values_topo; base; base = base->topo_next)
    {
      ipcp_value_source<valtype> *src;
      ipcp_value<valtype> *val;
      int time = 0, size = 0;

      for (val = base; val; val = val->scc_next)
	{
	  time = safe_add (time,
			   val->local_time_benefit + val->prop_time_benefit);
	  size = safe_add (size, val->local_size_cost + val->prop_size_cost);
	}

      for (val = base; val; val = val->scc_next)
	for (src = val->sources; src; src = src->next)
	  if (src->val
	      && src->cs->maybe_hot_p ())
	    {
	      src->val->prop_time_benefit = safe_add (time,
						src->val->prop_time_benefit);
	      src->val->prop_size_cost = safe_add (size,
						   src->val->prop_size_cost);
	    }
    }
}

Project: gcc

merge_aggregate_lattices (struct cgraph_edge *cs,
			  class ipcp_param_lattices *dest_plats,
			  class ipcp_param_lattices *src_plats,
			  int src_idx, HOST_WIDE_INT offset_delta)
{
  bool pre_existing = dest_plats->aggs != NULL;
  struct ipcp_agg_lattice **dst_aglat;
  bool ret = false;

  if (set_check_aggs_by_ref (dest_plats, src_plats->aggs_by_ref))
    return true;
  if (src_plats->aggs_bottom)
    return set_agg_lats_contain_variable (dest_plats);
  if (src_plats->aggs_contain_variable)
    ret |= set_agg_lats_contain_variable (dest_plats);
  dst_aglat = &dest_plats->aggs;

  for (struct ipcp_agg_lattice *src_aglat = src_plats->aggs;
       src_aglat;
       src_aglat = src_aglat->next)
    {
      HOST_WIDE_INT new_offset = src_aglat->offset - offset_delta;

      if (new_offset < 0)
	continue;
      if (merge_agg_lats_step (dest_plats, new_offset, src_aglat->size,
			       &dst_aglat, pre_existing, &ret))
	{
	  struct ipcp_agg_lattice *new_al = *dst_aglat;

	  dst_aglat = &(*dst_aglat)->next;
	  if (src_aglat->bottom)
	    {
	      ret |= new_al->set_contains_variable ();
	      continue;
	    }
	  if (src_aglat->contains_variable)
	    ret |= new_al->set_contains_variable ();
	  for (ipcp_value<tree> *val = src_aglat->values;
	       val;
	       val = val->next)
	    ret |= new_al->add_value (val->value, cs, val, src_idx,
				      src_aglat->offset);
	}
      else if (dest_plats->aggs_bottom)
	return true;
    }
  ret |= set_chain_of_aglats_contains_variable (*dst_aglat);
  return ret;
}

Project: gcc

ipcp_vr_lattice::meet_with (const ipcp_vr_lattice &other)
{
  return meet_with_1 (&other.m_vr);
}

Project: gcc

ipcp_write_summary (void)
{
  ipa_prop_write_jump_functions ();
}

Project: gcc

self_recursive_agg_pass_through_p (cgraph_edge *cs, ipa_agg_jf_item *jfunc,
				   int i)
{
  enum availability availability;
  if (cs->caller == cs->callee->function_symbol (&availability)
      && availability > AVAIL_INTERPOSABLE
      && jfunc->jftype == IPA_JF_LOAD_AGG
      && jfunc->offset == jfunc->value.load_agg.offset
      && jfunc->value.pass_through.operation == NOP_EXPR
      && jfunc->value.pass_through.formal_id == i)
    return true;
  return false;
}

Project: gcc

  bool top_p () { return m_lattice_val == IPA_BITS_UNDEFINED; }

Project: gcc

pop_node_from_stack (class ipa_topo_info *topo)
{
  if (topo->stack_top)
    {
      struct cgraph_node *node;
      topo->stack_top--;
      node = topo->stack[topo->stack_top];
      IPA_NODE_REF (node)->node_enqueued = 0;
      return node;
    }
  else
    return NULL;
}

Project: gcc

  widest_int get_value () { return m_value; }

Project: gcc

dump_profile_updates (struct cgraph_node *orig_node,
		      struct cgraph_node *new_node)
{
  struct cgraph_edge *cs;

  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 ");
  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");
    }
}

Project: gcc

propagate_scalar_across_jump_function (struct cgraph_edge *cs,
				       struct ipa_jump_func *jfunc,
				       ipcp_lattice<tree> *dest_lat,
				       tree param_type)
{
  if (dest_lat->bottom)
    return false;

  if (jfunc->type == IPA_JF_CONST)
    {
      tree val = ipa_get_jf_constant (jfunc);
      return dest_lat->add_value (val, cs, NULL, 0);
    }
  else if (jfunc->type == IPA_JF_PASS_THROUGH
	   || jfunc->type == IPA_JF_ANCESTOR)
    {
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      ipcp_lattice<tree> *src_lat;
      int src_idx;
      bool ret;

      if (jfunc->type == IPA_JF_PASS_THROUGH)
	src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
      else
	src_idx = ipa_get_jf_ancestor_formal_id (jfunc);

      src_lat = ipa_get_scalar_lat (caller_info, src_idx);
      if (src_lat->bottom)
	return dest_lat->set_contains_variable ();

      /* If we would need to clone the caller and cannot, do not propagate.  */
      if (!ipcp_versionable_function_p (cs->caller)
	  && (src_lat->contains_variable
	      || (src_lat->values_count > 1)))
	return dest_lat->set_contains_variable ();

      if (jfunc->type == IPA_JF_PASS_THROUGH)
	ret = propagate_vals_across_pass_through (cs, jfunc, src_lat,
						  dest_lat, src_idx, param_type);
      else
	ret = propagate_vals_across_ancestor (cs, jfunc, src_lat, dest_lat,
					      src_idx);

      if (src_lat->contains_variable)
	ret |= dest_lat->set_contains_variable ();

      return ret;
    }

  /* TODO: We currently do not handle member method pointers in IPA-CP (we only
     use it for indirect inlining), we should propagate them too.  */
  return dest_lat->set_contains_variable ();
}

Project: gcc

has_undead_caller_from_outside_scc_p (struct cgraph_node *node,
				      void *data ATTRIBUTE_UNUSED)
{
  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
	  (has_undead_caller_from_outside_scc_p, NULL, true))
      return true;
    else if (!ipa_edge_within_scc (cs)
	     && !IPA_NODE_REF (cs->caller)->node_dead)
      return true;
  return false;
}

Project: gcc

get_replacement_map (class ipa_node_params *info, tree value, int parm_num)
{
  struct ipa_replace_map *replace_map;


  replace_map = ggc_alloc<ipa_replace_map> ();
  if (dump_file)
    {
      fprintf (dump_file, "    replacing ");
      ipa_dump_param (dump_file, info, parm_num);

      fprintf (dump_file, " with const ");
      print_generic_expr (dump_file, value);
      fprintf (dump_file, "\n");
    }
  replace_map->parm_num = parm_num;
  replace_map->new_tree = value;
  return replace_map;
}

Project: gcc

cgraph_edge_brings_value_p (cgraph_edge *cs, ipcp_value_source<tree> *src,
			    cgraph_node *dest, ipcp_value<tree> *dest_val)
{
  class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
  enum availability availability;
  cgraph_node *real_dest = cs->callee->function_symbol (&availability);

  if (availability <= AVAIL_INTERPOSABLE
      || !same_node_or_its_all_contexts_clone_p (real_dest, dest)
      || caller_info->node_dead)
    return false;

  if (!src->val)
    return true;

  if (caller_info->ipcp_orig_node)
    {
      tree t;
      if (src->offset == -1)
	t = caller_info->known_csts[src->index];
      else
	t = get_clone_agg_value (cs->caller, src->offset, src->index);
      return (t != NULL_TREE
	      && values_equal_for_ipcp_p (src->val->value, t));
    }
  else
    {
      /* At the moment we do not propagate over arithmetic jump functions in
	 SCCs, so it is safe to detect self-feeding recursive calls in this
	 way.  */
      if (src->val == dest_val)
	return true;

      struct ipcp_agg_lattice *aglat;
      class ipcp_param_lattices *plats = ipa_get_parm_lattices (caller_info,
								 src->index);
      if (src->offset == -1)
	return (plats->itself.is_single_const ()
		&& values_equal_for_ipcp_p (src->val->value,
					    plats->itself.values->value));
      else
	{
	  if (plats->aggs_bottom || plats->aggs_contain_variable)
	    return false;
	  for (aglat = plats->aggs; aglat; aglat = aglat->next)
	    if (aglat->offset == src->offset)
	      return  (aglat->is_single_const ()
		       && values_equal_for_ipcp_p (src->val->value,
						   aglat->values->value));
	}
      return false;
    }
}

Project: gcc

  virtual unsigned int execute (function *) { return ipcp_driver (); }

Project: gcc

update_profiling_info (struct cgraph_node *orig_node,
		       struct cgraph_node *new_node)
{
  struct cgraph_edge *cs;
  struct caller_statistics stats;
  profile_count new_sum, orig_sum;
  profile_count remainder, orig_node_count = orig_node->count;
  profile_count orig_new_node_count = new_node->count;

  if (!(orig_node_count.ipa () > profile_count::zero ()))
    return;

  init_caller_stats (&stats);
  orig_node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
					       false);
  orig_sum = stats.count_sum;
  init_caller_stats (&stats);
  new_node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
					      false);
  new_sum = stats.count_sum;

  if (orig_node_count < orig_sum + new_sum)
    {
      if (dump_file)
	{
	  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 ");
	  orig_node_count.dump (dump_file);
	  fprintf (dump_file, "\n");
	}
    }

  remainder = orig_node_count.combine_with_ipa_count (orig_node_count.ipa ()
						      - new_sum.ipa ());

  /* With partial train run we do not want to assume that original's
     count is zero whenever we redurect all executed edges to clone.
     Simply drop profile to local one in this case.  */
  if (remainder.ipa_p () && !remainder.ipa ().nonzero_p ()
      && orig_node->count.ipa_p () && orig_node->count.ipa ().nonzero_p ()
      && flag_profile_partial_training)
    remainder = remainder.guessed_local ();

  new_sum = orig_node_count.combine_with_ipa_count (new_sum);
  new_node->count = new_sum;
  orig_node->count = remainder;

  profile_count::adjust_for_ipa_scaling (&new_sum, &orig_new_node_count);
  for (cs = new_node->callees; cs; cs = cs->next_callee)
    cs->count = cs->count.apply_scale (new_sum, orig_new_node_count);
  for (cs = new_node->indirect_calls; cs; cs = cs->next_callee)
    cs->count = cs->count.apply_scale (new_sum, orig_new_node_count);

  profile_count::adjust_for_ipa_scaling (&remainder, &orig_node_count);
  for (cs = orig_node->callees; cs; cs = cs->next_callee)
    cs->count = cs->count.apply_scale (remainder, orig_node_count);
  for (cs = orig_node->indirect_calls; 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);
}

Project: gcc

update_specialized_profile (struct cgraph_node *new_node,
			    struct cgraph_node *orig_node,
			    profile_count redirected_sum)
{
  struct cgraph_edge *cs;
  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 ");
      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 = new_node->count;
  new_node->count += redirected_sum;
  orig_node->count -= redirected_sum;

  for (cs = new_node->callees; cs; cs = cs->next_callee)
    cs->count += cs->count.apply_scale (redirected_sum, new_node_count);

  for (cs = orig_node->callees; cs; cs = cs->next_callee)
    {
      profile_count dec = cs->count.apply_scale (redirected_sum,
						 orig_node_count);
      cs->count -= dec;
    }

  if (dump_file)
    dump_profile_updates (orig_node, new_node);
}

Project: gcc

decide_whether_version_node (struct cgraph_node *node)
{
  class ipa_node_params *info = IPA_NODE_REF (node);
  int i, count = ipa_get_param_count (info);
  vec<tree> known_csts;
  vec<ipa_polymorphic_call_context> known_contexts;
  bool ret = false;

  if (count == 0)
    return false;

  if (dump_file && (dump_flags & TDF_DETAILS))
    fprintf (dump_file, "\nEvaluating opportunities for %s.\n",
	     node->dump_name ());

  gather_context_independent_values (info, &known_csts, &known_contexts,
				     NULL, NULL);

  for (i = 0; i < count;i++)
    {
      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
      ipcp_lattice<tree> *lat = &plats->itself;
      ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;

      if (!lat->bottom
	  && !known_csts[i])
	{
	  ipcp_value<tree> *val;
	  for (val = lat->values; val; val = val->next)
	    ret |= decide_about_value (node, i, -1, val, known_csts,
				       known_contexts);
	}

      if (!plats->aggs_bottom)
	{
	  struct ipcp_agg_lattice *aglat;
	  ipcp_value<tree> *val;
	  for (aglat = plats->aggs; aglat; aglat = aglat->next)
	    if (!aglat->bottom && aglat->values
		/* If the following is false, the one value is in
		   known_aggs.  */
		&& (plats->aggs_contain_variable
		    || !aglat->is_single_const ()))
	      for (val = aglat->values; val; val = val->next)
		ret |= decide_about_value (node, i, aglat->offset, val,
					   known_csts, known_contexts);
	}

      if (!ctxlat->bottom
	  && known_contexts[i].useless_p ())
	{
	  ipcp_value<ipa_polymorphic_call_context> *val;
	  for (val = ctxlat->values; val; val = val->next)
	    ret |= decide_about_value (node, i, -1, val, known_csts,
				       known_contexts);
	}

	info = IPA_NODE_REF (node);
    }

  if (info->do_clone_for_all_contexts)
    {
      struct cgraph_node *clone;
      vec<cgraph_edge *> callers;

      if (dump_file)
	fprintf (dump_file, " - Creating a specialized node of %s "
		 "for all known contexts.\n", node->dump_name ());

      callers = node->collect_callers ();
      find_more_scalar_values_for_callers_subset (node, known_csts, callers);
      find_more_contexts_for_caller_subset (node, &known_contexts, callers);
      ipa_agg_replacement_value *aggvals
	= find_aggregate_values_for_callers_subset (node, callers);

      if (!known_contexts_useful_p (known_contexts))
	{
	  known_contexts.release ();
	  known_contexts = vNULL;
	}
      clone = create_specialized_node (node, known_csts, known_contexts,
				       aggvals, callers);
      info = IPA_NODE_REF (node);
      info->do_clone_for_all_contexts = false;
      IPA_NODE_REF (clone)->is_all_contexts_clone = true;
      ret = true;
    }
  else
    {
      known_csts.release ();
      known_contexts.release ();
    }

  return ret;
}

Project: gcc

ipcp_versionable_function_p (struct cgraph_node *node)
{
  return IPA_NODE_REF (node) && IPA_NODE_REF (node)->versionable;
}

Project: gcc

propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc,
				   class 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)
    {
      enum tree_code operation = ipa_get_jf_pass_through_operation (jfunc);
      class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
      int src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
      class ipcp_param_lattices *src_lats
	= ipa_get_parm_lattices (caller_info, src_idx);
      tree operand_type = ipa_get_type (caller_info, src_idx);

      if (src_lats->m_value_range.bottom_p ())
	return dest_lat->set_to_bottom ();

      value_range vr;
      if (TREE_CODE_CLASS (operation) == tcc_unary)
	ipa_vr_operation_and_type_effects (&vr,
					   &src_lats->m_value_range.m_vr,
					   operation, param_type,
					   operand_type);
      /* A crude way to prevent unbounded number of value range updates
	 in SCC components.  We should allow limited number of updates within
	 SCC, too.  */
      else if (!ipa_edge_within_scc (cs))
	{
	  tree op = ipa_get_jf_pass_through_operand (jfunc);
	  value_range op_vr (op, op);
	  value_range op_res,res;

	  range_fold_binary_expr (&op_res, operation, operand_type,
				  &src_lats->m_value_range.m_vr, &op_vr);
	  ipa_vr_operation_and_type_effects (&vr,
					     &op_res,
					     NOP_EXPR, param_type,
					     operand_type);
	}
      if (!vr.undefined_p () && !vr.varying_p ())
	{
	  if (jfunc->m_vr)
	    {
	      value_range jvr;
	      if (ipa_vr_operation_and_type_effects (&jvr, jfunc->m_vr,
						     NOP_EXPR,
						     param_type,
						     jfunc->m_vr->type ()))
		vr.intersect (jvr);
	    }
	  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 (val, val);
	  return dest_lat->meet_with (&tmpvr);
	}
    }

  value_range vr;
  if (jfunc->m_vr
      && ipa_vr_operation_and_type_effects (&vr, jfunc->m_vr, NOP_EXPR,
					    param_type,
					    jfunc->m_vr->type ()))
    return dest_lat->meet_with (&vr);
  else
    return dest_lat->set_to_bottom ();
}

Project: gcc

find_aggregate_values_for_callers_subset (struct cgraph_node *node,
					  vec<cgraph_edge *> callers)
{
  class ipa_node_params *dest_info = IPA_NODE_REF (node);
  struct ipa_agg_replacement_value *res;
  struct ipa_agg_replacement_value **tail = &res;
  struct cgraph_edge *cs;
  int i, j, count = ipa_get_param_count (dest_info);

  FOR_EACH_VEC_ELT (callers, j, cs)
    {
      if (!IPA_EDGE_REF (cs))
	{
	  count = 0;
	  break;
	}
      int c = ipa_get_cs_argument_count (IPA_EDGE_REF (cs));
      if (c < count)
	count = c;
    }

  for (i = 0; i < count; i++)
    {
      struct cgraph_edge *cs;
      vec<ipa_agg_value> inter = vNULL;
      struct ipa_agg_value *item;
      class ipcp_param_lattices *plats = ipa_get_parm_lattices (dest_info, i);
      int j;

      /* Among other things, the following check should deal with all by_ref
	 mismatches.  */
      if (plats->aggs_bottom)
	continue;

      FOR_EACH_VEC_ELT (callers, j, cs)
	{
	  struct ipa_jump_func *jfunc
	    = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
	  if (self_recursive_pass_through_p (cs, jfunc, i)
	      && (!plats->aggs_by_ref
		  || ipa_get_jf_pass_through_agg_preserved (jfunc)))
	    continue;
	  inter = intersect_aggregates_with_edge (cs, i, inter);

	  if (!inter.exists ())
	    goto next_param;
	}

      FOR_EACH_VEC_ELT (inter, j, item)
	{
	  struct ipa_agg_replacement_value *v;

	  if (!item->value)
	    continue;

	  /* All values must be real values, not unknown place holders.  */
	  gcc_assert (item->value != error_mark_node);

	  v = ggc_alloc<ipa_agg_replacement_value> ();
	  v->index = i;
	  v->offset = item->offset;
	  v->value = item->value;
	  v->by_ref = plats->aggs_by_ref;
	  *tail = v;
	  tail = &v->next;
	}

    next_param:
      if (inter.exists ())
	inter.release ();
    }
  *tail = NULL;
  return res;
}

Project: gcc

spread_undeadness (struct cgraph_node *node)
{
  struct cgraph_edge *cs;

  for (cs = node->callees; cs; cs = cs->next_callee)
    if (ipa_edge_within_scc (cs))
      {
	struct cgraph_node *callee;
	class ipa_node_params *info;

	callee = cs->callee->function_symbol (NULL);
	info = IPA_NODE_REF (callee);

	if (info && info->node_dead)
	  {
	    info->node_dead = 0;
	    spread_undeadness (callee);
	  }
      }
}

Project: gcc

set_agg_lats_to_bottom (class ipcp_param_lattices *plats)
{
  bool ret = !plats->aggs_bottom;
  plats->aggs_bottom = true;
  return ret;
}

Project: gcc

self_recursive_pass_through_p (cgraph_edge *cs, ipa_jump_func *jfunc, int i)
{
  enum availability availability;
  if (cs->caller == cs->callee->function_symbol (&availability)
      && availability > AVAIL_INTERPOSABLE
      && jfunc->type == IPA_JF_PASS_THROUGH
      && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR
      && ipa_get_jf_pass_through_formal_id (jfunc) == i)
    return true;
  return false;
}

Project: gcc

  edge_clone_summary_t (symbol_table *symtab):
    call_summary <edge_clone_summary *> (symtab)
    {
      m_initialize_when_cloning = true;
    }

Project: gcc

good_cloning_opportunity_p (struct cgraph_node *node, int time_benefit,
			    int freq_sum, profile_count count_sum, int size_cost)
{
  if (time_benefit == 0
      || !opt_for_fn (node->decl, flag_ipa_cp_clone)
      || node->optimize_for_size_p ())
    return false;

  gcc_assert (size_cost > 0);

  class ipa_node_params *info = IPA_NODE_REF (node);
  if (max_count > profile_count::zero ())
    {
      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: ", time_benefit, size_cost);
	  count_sum.dump (dump_file);
	  fprintf (dump_file, "%s%s) -> evaluation: " "%" PRId64
		 ", threshold: %i\n",
		 info->node_within_scc
		   ? (info->node_is_self_scc ? ", self_scc" : ", scc") : "",
		 info->node_calling_single_call ? ", single_call" : "",
		 evaluation, param_ipa_cp_eval_threshold);
	}

      return evaluation >= param_ipa_cp_eval_threshold;
    }
  else
    {
      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%s%s) -> evaluation: "
		 "%" PRId64 ", threshold: %i\n",
		 time_benefit, size_cost, freq_sum,
		 info->node_within_scc
		   ? (info->node_is_self_scc ? ", self_scc" : ", scc") : "",
		 info->node_calling_single_call ? ", single_call" : "",
		 evaluation, param_ipa_cp_eval_threshold);

      return evaluation >= param_ipa_cp_eval_threshold;
    }
}

Project: gcc

copy_useful_known_contexts (vec<ipa_polymorphic_call_context> known_contexts)
{
  if (known_contexts_useful_p (known_contexts))
    return known_contexts.copy ();
  else
    return vNULL;
}

Project: gcc

init_caller_stats (struct caller_statistics *stats)
{
  stats->count_sum = profile_count::zero ();
  stats->n_calls = 0;
  stats->n_hot_calls = 0;
  stats->freq_sum = 0;
}

Project: gcc

ipa_get_poly_ctx_lat (class ipa_node_params *info, int i)
{
  class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
  return &plats->ctxlat;
}

Project: gcc

print_ipcp_constant_value (FILE * f, tree v)
{
  if (TREE_CODE (v) == ADDR_EXPR
      && TREE_CODE (TREE_OPERAND (v, 0)) == CONST_DECL)
    {
      fprintf (f, "& ");
      print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (v, 0)));
    }
  else
    print_generic_expr (f, v);
}