Ping: [PATCH V5] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
diff mbox series

Message ID BYAPR01MB486902C62A4AAB0F8246ACBCF77F0@BYAPR01MB4869.prod.exchangelabs.com
State New
Headers show
Series
  • Ping: [PATCH V5] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
Related show

Commit Message

Feng Xue OS Nov. 4, 2019, 9:39 a.m. UTC
Hi, Honza & Martin,

  This is a new patch merged with the newest IPA changes. Would you please take a look at the patch?
Together with the other patch on recursive function versioning, we can find more than 30% performance
boost on exchange2 in spec2017. So, it will be good if two patches can enter the gcc 10 release,
though time schedule seems to be somewhat urgent. Thanks.

Feng

Comments

Martin Jambor Nov. 5, 2019, 1:40 a.m. UTC | #1
Hi,

On Mon, Nov 04 2019, Feng Xue OS wrote:
> Hi, Honza & Martin,
>
> This is a new patch merged with the newest IPA changes. Would you
> please take a look at the patch?  Together with the other patch on
> recursive function versioning, we can find more than 30% performance
> boost on exchange2 in spec2017. So, it will be good if two patches can
> enter the gcc 10 release, though time schedule seems to be somewhat
> urgent. Thanks.

Sorry that it took so long.  Next time, please consider making the
review a bit easier by writing a ChangeLog (yes, I usually read them and
you'll have to write one anyway).

The good news is that I found no real objection to the patch, I'd only
like to request a few minor changes.  Thanks a lot for working on this,
this extension of aggregate jump functions is really appreciated.  But
it also shows that we really need to beat a bit more sense into the
various involved data structures now since they grew a bit out of hand.
Nevertheless, that is something for next stage1 and should not block
this.

Anyway, comments inline, there is really just a few of them.  Honza,
please have a final look but overall I like the patch.

>
> Feng
>
> From 2dfa5e8b5a828ad8d46c2af5f66ee97fb04ebc16 Mon Sep 17 00:00:00 2001
> From: Feng Xue <fxue@os.amperecomputing.com>
> Date: Thu, 15 Aug 2019 15:47:14 +0800
> Subject: [PATCH 1/2] temp
>
> ---
>  gcc/ipa-cp.c                           | 498 ++++++++++++++++------
>  gcc/ipa-fnsummary.c                    |  48 +--
>  gcc/ipa-fnsummary.h                    |   8 +-
>  gcc/ipa-inline-analysis.c              |   6 +-
>  gcc/ipa-prop.c                         | 569 +++++++++++++++++++++----
>  gcc/ipa-prop.h                         | 182 ++++++--
>  gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c |   8 +-
>  gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c |  77 ++++
>  8 files changed, 1105 insertions(+), 291 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
>
> diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
> index 8a5f8d362f6..e100c5f3426 100644
> --- a/gcc/ipa-cp.c
> +++ b/gcc/ipa-cp.c
> @@ -1459,6 +1473,146 @@ ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx,
>    return ctx;
>  }
>  
> +/* See if NODE is a clone with a known aggregate value at a given OFFSET of a
> +   parameter with the given INDEX.  */
> +
> +static tree
> +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;
> +}
> +
> +/* Determine whether ITEM, jump function for an aggregate part, evaluates to a
> +   single known constant value and if so, return it.  Otherwise return NULL.
> +   NODE and INFO describes the caller node or the one it is inlined to, and
> +   its related info.  */
> +
> +static tree
> +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);

Wrong indentation for GNU coding standard.

> +
> +      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);
> +}
> +
> +/* Determine whether AGG_JFUNC evaluates to a set of known constant value for
> +   an aggregate and if so, return it.  Otherwise return an empty set.  NODE
> +   and INFO describes the caller node or the one it is inlined to, and its
> +   related info.  */
> +
> +struct ipa_agg_value_set
> +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;
> +}
> +
>  /* If checking is enabled, verify that no lattice is in the TOP state, i.e. not
>     bottom, not containing a variable component and without any known value at
>     the same time.  */

...

> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index 83cf4d1c7ba..194c31ff54a 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -1454,25 +1542,31 @@ get_ssa_def_if_simple_copy (tree rhs)
>  	rhs = gimple_assign_rhs1 (def_stmt);
>        else
>  	break;
> +      *rhs_stmt = def_stmt;
>      }
>    return rhs;
>  }
>  
> -/* Simple linked list, describing known contents of an aggregate before
> -   call.  */
> +/* Simple linked list, describing contents of an aggregate before call.  */
>  
>  struct ipa_known_agg_contents_list
>  {
>    /* Offset and size of the described part of the aggregate.  */
>    HOST_WIDE_INT offset, size;
> -  /* Known constant value or NULL if the contents is known to be unknown.  */
> -  tree constant;
> +
> +  /* Type of the described part of the aggregate.  */
> +  tree type;
> +
> +  /* Known constant value or jump function data describing contents.  */
> +  struct ipa_load_agg_data value;

I wonder whether it would be cleaner to repeat the fields of
ipa_load_agg_dat here.  But I don't insist.

> +
>    /* Pointer to the next structure in the list.  */
>    struct ipa_known_agg_contents_list *next;
>  };
>  
> -/* Add a known content item into a linked list of ipa_known_agg_contents_list
> -   structure, in which all elements are sorted ascendingly by offset.  */
> +/* Add an aggregate content item into a linked list of
> +   ipa_known_agg_contents_list structure, in which all elements
> +   are sorted ascendingly by offset.  */
>  
>  static inline void
>  add_to_agg_contents_list (struct ipa_known_agg_contents_list **plist,
> @@ -1492,7 +1586,7 @@ add_to_agg_contents_list (struct ipa_known_agg_contents_list **plist,
>    *plist = item;
>  }
>  
> -/* Check whether a given known content is clobbered by certain element in
> +/* Check whether a given aggregate content is clobbered by certain element in
>     a linked list of ipa_known_agg_contents_list.  */
>  
>  static inline bool
> @@ -1512,27 +1606,189 @@ clobber_by_agg_contents_list_p (struct ipa_known_agg_contents_list *list,
>  }
>  
>  /* Build aggregate jump function from LIST, assuming there are exactly
> -   CONST_COUNT constant entries there and that offset of the passed argument
> +   VALUE_COUNT entries there and that offset of the passed argument
>     is ARG_OFFSET and store it into JFUNC.  */
>  
>  static void
>  build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list,
> -			       int const_count, HOST_WIDE_INT arg_offset,
> +			       int value_count, HOST_WIDE_INT arg_offset,
>  			       struct ipa_jump_func *jfunc)
>  {
> -  vec_alloc (jfunc->agg.items, const_count);
> -  while (list)
> +  vec_alloc (jfunc->agg.items, value_count);
> +  for (; list; list = list->next)
> +    {
> +      struct ipa_agg_jf_item item;
> +      tree operand = list->value.pass_through.operand;
> +
> +      if (list->value.pass_through.formal_id >= 0)
> +	{
> +	  /* Content value is derived from some formal paramerter.  */
> +	  if (list->value.offset >= 0)
> +	    item.jftype = IPA_JF_LOAD_AGG;
> +	  else
> +	    item.jftype = IPA_JF_PASS_THROUGH;
> +
> +	  item.value.load_agg = list->value;
> +	  if (operand)
> +	    item.value.pass_through.operand
> +				= unshare_expr_without_location (operand);

Wrong indentation for GNU coding standard.

> +	}
> +      else if (operand)
> +	{
> +	  /* Content value is known constant.  */
> +	  item.jftype = IPA_JF_CONST;
> +	  item.value.constant = unshare_expr_without_location (operand);
> +	}
> +      else
> +	continue;
> +
> +      item.type = list->type;
> +      gcc_assert (tree_to_shwi (TYPE_SIZE (list->type)) == list->size);
> +
> +      item.offset = list->offset - arg_offset;
> +      gcc_assert ((item.offset % BITS_PER_UNIT) == 0);
> +
> +      jfunc->agg.items->quick_push (item);
> +    }
> +}
> +
> +/* Given an assignment statement STMT, try to collect information into
> +   AGG_VALUE that will be used to construct jump function for RHS of the
> +   assignment, from which content value of an aggregate part comes.
> +
> +   Besides constant and simple pass-through jump functions, also try to
> +   identify whether it matches the following pattern that can be described by
> +   a load-value-from-aggregate jump function, which is a derivative of simple
> +   pass-through jump function.
> +
> +     foo (int *p)
> +     {
> +       ...
> +
> +       *(q_5 + 4) = *(p_3(D) + 28) op 1;
> +       bar (q_5);
> +     }
> +
> +   Since load-value-from-aggregate jump function data structure is informative
> +   enough to describe constant and simple pass-through jump function, here we
> +   do not need a jump function type, merely use FORMAL_ID and OPERAND in
> +   IPA_LOAD_AGG_DATA to distinguish different jump functions.  */

This last comment is difficult to understand to the point when IMHO one
has to read the code anyway.  Perhaps you could just list which special
values imply which final jump function type?  And perhaps that list
should go to the comment describing ipa_known_agg_contents_list.

> +
> +static void
> +compute_assign_agg_jump_func (struct ipa_func_body_info *fbi,
> +			      struct ipa_load_agg_data *agg_value,

My preference would be for this function to receive a pointer to the
whole ipa_known_agg_contents_list as a parameter instead of agg_value,
and to be called something like extract_agg_content_or_origin or
something that would not suggest it creates a real jump function.
Please at least consider changing the name.

> +			      gimple *stmt)
> +{
> +  tree lhs = gimple_assign_lhs (stmt);
> +  tree rhs1 = gimple_assign_rhs1 (stmt);
> +  enum tree_code code;
> +  int index = -1;
> +
> +  /* Initialize jump function data for the aggregate part.  */
> +  memset (agg_value, 0, sizeof (*agg_value));
> +  agg_value->pass_through.operation = NOP_EXPR;
> +  agg_value->pass_through.formal_id = -1;
> +  agg_value->offset = -1;
> +
> +  if (AGGREGATE_TYPE_P (TREE_TYPE (lhs))  /* TODO: Support aggregate type.  */
> +      || TREE_THIS_VOLATILE (lhs)
> +      || TREE_CODE (lhs) == BIT_FIELD_REF
> +      || contains_bitfld_component_ref_p (lhs))
> +    return;
> +
> +  /* Skip SSA copies.  */
> +  while (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS)
> +    {
> +      if (TREE_CODE (rhs1) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (rhs1))
> +	break;
> +
> +      if (!is_gimple_assign (stmt = SSA_NAME_DEF_STMT (rhs1)))

Please put the assignment into a separate statement.

> +	return;
> +
> +      rhs1 = gimple_assign_rhs1 (stmt);
> +    }
> +
> +  code = gimple_assign_rhs_code (stmt);
> +  switch (gimple_assign_rhs_class (stmt))
>      {
> -      if (list->constant)
> +    case GIMPLE_SINGLE_RHS:
> +      if (is_gimple_ip_invariant (rhs1))
>  	{
> -	  struct ipa_agg_jf_item item;
> -	  item.offset = list->offset - arg_offset;
> -	  gcc_assert ((item.offset % BITS_PER_UNIT) == 0);
> -	  item.value = unshare_expr_without_location (list->constant);
> -	  jfunc->agg.items->quick_push (item);
> +	  agg_value->pass_through.operand = rhs1;
> +	  return;
>  	}
> -      list = list->next;
> +      code = NOP_EXPR;
> +      break;
> +
> +    case GIMPLE_UNARY_RHS:
> +      /* NOTE: A GIMPLE_UNARY_RHS operation might not be tcc_unary
> +	 (truth_not_expr is example), GIMPLE_BINARY_RHS does not imply
> +	 tcc_binary, this subtleness is somewhat misleading.
> +
> +	 Since tcc_unary is widely used in IPA-CP code to check an operation
> +	 with one operand, here we only allow tc_unary operation to avoid
> +	 possible problem.  Then we can use (opclass == tc_unary) or not to
> +	 distinguish unary and binary.  */
> +      if (TREE_CODE_CLASS (code) != tcc_unary || CONVERT_EXPR_CODE_P (code))
> +	return;
> +
> +      rhs1 = get_ssa_def_if_simple_copy (rhs1, &stmt);
> +      break;
> +
> +    case GIMPLE_BINARY_RHS:
> +      {
> +	gimple *rhs1_stmt = stmt;
> +	gimple *rhs2_stmt = stmt;
> +	tree rhs2 = gimple_assign_rhs2 (stmt);
> +
> +	rhs1 = get_ssa_def_if_simple_copy (rhs1, &rhs1_stmt);
> +	rhs2 = get_ssa_def_if_simple_copy (rhs2, &rhs2_stmt);
> +
> +	if (is_gimple_ip_invariant (rhs2))
> +	  {
> +	    agg_value->pass_through.operand = rhs2;
> +	    stmt = rhs1_stmt;
> +	  }
> +	else if (is_gimple_ip_invariant (rhs1))
> +	  {
> +	    if (TREE_CODE_CLASS (code) == tcc_comparison)
> +	      code = swap_tree_comparison (code);
> +	    else if (!commutative_tree_code (code))
> +	      return;
> +
> +	    agg_value->pass_through.operand = rhs1;
> +	    stmt = rhs2_stmt;
> +	    rhs1 = rhs2;
> +	  }
> +	else
> +	  return;
> +
> +	if (TREE_CODE_CLASS (code) != tcc_comparison
> +	    && !useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs1)))
> +	  return;
> +      }
> +      break;
> +
> +    default:
> +      return;
> +  }
> +
> +  if (TREE_CODE (rhs1) != SSA_NAME)
> +    index = load_from_unmodified_param_or_agg (fbi, fbi->info, stmt,
> +					       &agg_value->offset,
> +					       &agg_value->by_ref);
> +  else if (SSA_NAME_IS_DEFAULT_DEF (rhs1))
> +    index = ipa_get_param_decl_index (fbi->info, SSA_NAME_VAR (rhs1));
> +
> +  if (index >= 0)
> +    {
> +      if (agg_value->offset >= 0)
> +	agg_value->type = TREE_TYPE (rhs1);
> +      agg_value->pass_through.formal_id = index;
> +      agg_value->pass_through.operation = code;
>      }
> +  else
> +    agg_value->pass_through.operand = NULL_TREE;
>  }
>  
>  /* If STMT is a memory store to the object whose address is BASE, extract
> @@ -1542,26 +1798,19 @@ build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list,
>     is expected to be in form of MEM_REF expression.  */
>  
>  static bool
> -extract_mem_content (gimple *stmt, tree base, bool check_ref,
> +extract_mem_content (struct ipa_func_body_info *fbi,
> +		     gimple *stmt, tree base, bool check_ref,
>  		     struct ipa_known_agg_contents_list *content)
>  {
>    HOST_WIDE_INT lhs_offset, lhs_size;
> -  tree lhs, rhs, lhs_base;
>    bool reverse;
>  
> -  if (!gimple_assign_single_p (stmt))
> -    return false;
> -
> -  lhs = gimple_assign_lhs (stmt);
> -  rhs = gimple_assign_rhs1 (stmt);
> -
> -  if (!is_gimple_reg_type (TREE_TYPE (rhs))
> -      || TREE_CODE (lhs) == BIT_FIELD_REF
> -      || contains_bitfld_component_ref_p (lhs))
> +  if (!is_gimple_assign (stmt))
>      return false;
>  
> -  lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset,
> -					  &lhs_size, &reverse);
> +  tree lhs = gimple_assign_lhs (stmt);
> +  tree lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset, &lhs_size,
> +					       &reverse);
>    if (!lhs_base)
>      return false;
>  
> @@ -1575,32 +1824,31 @@ extract_mem_content (gimple *stmt, tree base, bool check_ref,
>    else if (lhs_base != base)
>      return false;
>  
> -  rhs = get_ssa_def_if_simple_copy (rhs);
> -
> -  content->size = lhs_size;
>    content->offset = lhs_offset;
> -  content->constant = is_gimple_ip_invariant (rhs) ? rhs : NULL_TREE;
> +  content->size = lhs_size;
> +  content->type = TREE_TYPE (lhs);
>    content->next = NULL;
>  
> +  compute_assign_agg_jump_func (fbi, &content->value, stmt);
>    return true;
>  }
>  
>  /* Traverse statements from CALL backwards, scanning whether an aggregate given
> -   in ARG is filled in with constant values.  ARG can either be an aggregate
> -   expression or a pointer to an aggregate.  ARG_TYPE is the type of the
> -   aggregate.  JFUNC is the jump function into which the constants are
> -   subsequently stored.  AA_WALK_BUDGET_P points to limit on number of
> -   statements we allow get_continuation_for_phi to examine.  */
> +   in ARG is filled in constant or value that is derived from caller's formal

Minor nit, please use plural "constants or values that are..."

> +   parameter in the way described by some kind of jump function.  FBI is the
> +   context of the caller function for interprocedural analysis.  ARG can either
> +   be an aggregate expression or a pointer to an aggregate.  ARG_TYPE is the
> +   type of the aggregate.  JFUNC is the jump function for the aggregate.  */
>  
>  static void
> -determine_known_aggregate_parts (gcall *call, tree arg,
> +determine_known_aggregate_parts (struct ipa_func_body_info *fbi,
> +				 gcall *call, tree arg,
>  				 tree arg_type,
> -				 struct ipa_jump_func *jfunc,
> -				 unsigned *aa_walk_budget_p)
> +				 struct ipa_jump_func *jfunc)
>  {
>    struct ipa_known_agg_contents_list *list = NULL, *all_list = NULL;
>    bitmap visited = NULL;
> -  int item_count = 0, const_count = 0;
> +  int item_count = 0, value_count = 0;
>    int ipa_max_agg_items = PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS);
>    HOST_WIDE_INT arg_offset, arg_size;
>    tree arg_base;
> @@ -1679,7 +1927,7 @@ determine_known_aggregate_parts (gcall *call, tree arg,
>        if (gimple_code (stmt) == GIMPLE_PHI)
>  	{
>  	  dom_vuse = get_continuation_for_phi (stmt, &r, true,
> -					       *aa_walk_budget_p,
> +					       fbi->aa_walk_budget,
>  					       &visited, false, NULL, NULL);
>  	  continue;
>  	}
> @@ -1689,12 +1937,13 @@ determine_known_aggregate_parts (gcall *call, tree arg,
>  	  struct ipa_known_agg_contents_list *content
>  			= XALLOCA (struct ipa_known_agg_contents_list);
>  
> -	  if (!extract_mem_content (stmt, arg_base, check_ref, content))
> +	  if (!extract_mem_content (fbi, stmt, arg_base, check_ref, content))
>  	    break;
>  
>  	  /* Now we get a dominating virtual operand, and need to check
>  	     whether its value is clobbered any other dominating one.  */
> -	  if (content->constant
> +	  if ((content->value.pass_through.formal_id >= 0
> +	       || content->value.pass_through.operand)
>  	      && !clobber_by_agg_contents_list_p (all_list, content))
>  	    {
>  	      struct ipa_known_agg_contents_list *copy
> @@ -1704,7 +1953,7 @@ determine_known_aggregate_parts (gcall *call, tree arg,
>  		 operands, whose definitions can finally reach the call.  */
>  	      add_to_agg_contents_list (&list, (*copy = *content, copy));
>  
> -	      if (++const_count == ipa_max_agg_items)
> +	      if (++value_count == ipa_max_agg_items)
>  		break;
>  	    }
>  
> @@ -1722,12 +1971,12 @@ determine_known_aggregate_parts (gcall *call, tree arg,
>  
>    /* Third stage just goes over the list and creates an appropriate vector of
>       ipa_agg_jf_item structures out of it, of course only if there are
> -     any known constants to begin with.  */
> +     any meaningful items to begin with.  */
>  
> -  if (const_count)
> +  if (value_count)
>      {
>        jfunc->agg.by_ref = by_ref;
> -      build_agg_jump_func_from_list (list, const_count, arg_offset, jfunc);
> +      build_agg_jump_func_from_list (list, value_count, arg_offset, jfunc);
>      }
>  }
>  
> @@ -2019,8 +2268,7 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi,
>  	      || !ipa_get_jf_ancestor_agg_preserved (jfunc))
>  	  && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
>  	      || POINTER_TYPE_P (param_type)))
> -	determine_known_aggregate_parts (call, arg, param_type, jfunc,
> -					 &fbi->aa_walk_budget);
> +	determine_known_aggregate_parts (fbi, call, arg, param_type, jfunc);
>      }
>    if (!useful_context)
>      vec_free (args->polymorphic_call_contexts);
> @@ -2679,6 +2927,72 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
>        class ipa_polymorphic_call_context *dst_ctx
>  	= ipa_get_ith_polymorhic_call_context (args, i);
>  
> +      if (dst->agg.items)
> +	{
> +	  struct ipa_agg_jf_item *item;
> +	  int j;
> +
> +	  FOR_EACH_VEC_ELT (*dst->agg.items, j, item)
> +	    {
> +	      int dst_fid;
> +	      struct ipa_jump_func *src;
> +
> +	      if (item->jftype != IPA_JF_PASS_THROUGH
> +		  && item->jftype != IPA_JF_LOAD_AGG)
> +		continue;
> +
> +	      dst_fid = item->value.pass_through.formal_id;
> +	      if (dst_fid >= ipa_get_cs_argument_count (top))
> +		{
> +		  item->jftype = IPA_JF_UNKNOWN;
> +		  continue;
> +		}
> +
> +	      item->value.pass_through.formal_id = -1;
> +	      src = ipa_get_ith_jump_func (top, dst_fid);
> +	      if (src->type == IPA_JF_CONST)
> +		{
> +		  if (item->jftype == IPA_JF_PASS_THROUGH
> +		      && item->value.pass_through.operation == NOP_EXPR)
> +		    {
> +		      item->jftype = IPA_JF_CONST;
> +		      item->value.constant = src->value.constant.value;
> +		      continue;
> +		    }
> +		}
> +	      else if (src->type == IPA_JF_PASS_THROUGH
> +		       && src->value.pass_through.operation == NOP_EXPR)
> +		{
> +		  if (item->jftype == IPA_JF_PASS_THROUGH
> +		      || !item->value.load_agg.by_ref
> +		      || src->value.pass_through.agg_preserved)
> +		    item->value.pass_through.formal_id
> +				= src->value.pass_through.formal_id;
> +		}
> +	      else if (src->type == IPA_JF_ANCESTOR)
> +		{
> +		  if (item->jftype == IPA_JF_PASS_THROUGH)
> +		    {
> +		      if (!src->value.ancestor.offset)
> +			item->value.pass_through.formal_id
> +				= src->value.ancestor.formal_id;
> +		    }
> +		  else if (src->value.ancestor.agg_preserved)
> +		    {
> +		      gcc_checking_assert (item->value.load_agg.by_ref);
> +
> +		      item->value.pass_through.formal_id
> +				 = src->value.ancestor.formal_id;
> +		      item->value.load_agg.offset
> +				+= src->value.ancestor.offset;
> +		    }
> +		}
> +
> +	      if (item->value.pass_through.formal_id < 0)
> +		item->jftype = IPA_JF_UNKNOWN;
> +	    }
> +	}
> +
>        if (dst->type == IPA_JF_ANCESTOR)
>  	{
>  	  struct ipa_jump_func *src;
> @@ -2718,8 +3032,11 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
>  		}
>  	    }
>  
> -	  if (src->agg.items
> -	      && (dst->value.ancestor.agg_preserved || !src->agg.by_ref))
> +	  /* Parameter and argument in ancestor jump function must be pointer
> +	     type, which means access to aggregate must be by-reference.  */
> +	  gcc_checking_assert (!src->agg.items || src->agg.by_ref);

I am slightly afraid that some type mismatches in between the call
statement fntype and callee type which are possible with LTO (and
horribly bad user input) might trigger this.  Please make this a
non-checking assert so that we find out if that is indeed true.

> +
> +	  if (src->agg.items && dst->value.ancestor.agg_preserved)
>  	    {
>  	      struct ipa_agg_jf_item *item;
>  	      int j;

...

> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index 9f2479e7fdc..cb54d2547e1 100644
> --- a/gcc/ipa-prop.h
> +++ b/gcc/ipa-prop.h
> @@ -39,6 +39,15 @@ along with GCC; see the file COPYING3.  If not see
>                    argument.
>     Unknown      - neither of the above.
>  
> +   IPA_JF_LOAD_AGG is a compound pass-through jump function, in which primary
> +   operation on formal parameter is memory dereference that loads a value from
> +   a part of an aggregate, which is represented or pointed to by the formal
> +   parameter.  Moreover, an additional unary/binary operation can be applied on
> +   the loaded value, and final result is passed as actual argument of callee
> +   (e.g. *(param_1(D) + 4) op 24 ).  It is meant to describe usage of aggregate
> +   parameter or by-reference parameter referenced in argument passing, commonly
> +   found in C++ and Fortran.
> +
>     IPA_JF_ANCESTOR is a special pass-through jump function, which means that
>     the result is an address of a part of the object pointed to by the formal
>     parameter to which the function refers.  It is mainly intended to represent
> @@ -60,6 +69,7 @@ enum jump_func_type
>    IPA_JF_UNKNOWN = 0,  /* newly allocated and zeroed jump functions default */
>    IPA_JF_CONST,             /* represented by field costant */
>    IPA_JF_PASS_THROUGH,	    /* represented by field pass_through */
> +  IPA_JF_LOAD_AGG,	    /* represented by field load_agg */
>    IPA_JF_ANCESTOR	    /* represented by field ancestor */
>  };
>  
> @@ -97,6 +107,26 @@ struct GTY(()) ipa_pass_through_data
>    unsigned agg_preserved : 1;
>  };
>  
> +/* Structure holding data required to describe a load-value-from-aggregate
> +   jump function.  */
> +
> +struct GTY(()) ipa_load_agg_data
> +{
> +  /* Inherit from pass through jump function, describing unary/binary
> +     operation on the value loaded from aggregate that is represented or
> +     pointed to by the formal parameter, specified by formal_id in this
> +     pass_through jump function data structure.  */
> +  struct ipa_pass_through_data pass_through;
> +  /* Type of the value loaded from the aggregate.  */
> +  tree type;
> +  /* Offset at which the value is located within the aggregate.  */
> +  HOST_WIDE_INT offset;
> +  /* True if loaded by reference (the aggregate is pointed to by the formal
> +     parameter) or false if loaded by value (the aggregate is represented
> +     by the formal parameter).  */
> +  bool by_ref;
> +};
> +
>  /* Structure holding data required to describe an ancestor pass-through
>     jump function.  */
>  
> @@ -110,58 +140,139 @@ struct GTY(()) ipa_ancestor_jf_data
>    unsigned agg_preserved : 1;
>  };
>  
> -/* An element in an aggegate part of a jump function describing a known value
> -   at a given offset.  When it is part of a pass-through jump function with
> -   agg_preserved set or an ancestor jump function with agg_preserved set, all
> -   unlisted positions are assumed to be preserved but the value can be a type
> -   node, which means that the particular piece (starting at offset and having
> -   the size of the type) is clobbered with an unknown value.  When
> -   agg_preserved is false or the type of the containing jump function is
> -   different, all unlisted parts are assumed to be unknown and all values must
> -   fulfill is_gimple_ip_invariant.  */
> +/* A jump function for an aggregate part at a given offset, which describes how
> +   it content value is generated.  All unlisted positions are assumed to have a
> +   value defined in an unknown way.  */
>  
>  struct GTY(()) ipa_agg_jf_item
>  {
> -  /* The offset at which the known value is located within the aggregate.  */
> +  /* The offset for the aggregate part.  */
>    HOST_WIDE_INT offset;
>  
> -  /* The known constant or type if this is a clobber.  */
> -  tree value;
> +  /* Data type of the aggregate part.  */
> +  tree type;
>  
> -  /* Return true if OTHER describes same agg item.  */
> -  bool equal_to (const ipa_agg_jf_item &other);
> -};
> +  /* Jump function type.  */
> +  enum jump_func_type jftype;
>  
> +  /* Represents a value of jump function. constant represents the actual constant
> +     in constant jump function content.  pass_through is used only in simple pass
> +     through jump function context.  load_agg is for load-value-from-aggregate
> +     jump function context.  */
> +  union jump_func_agg_value
> +  {
> +    tree GTY ((tag ("IPA_JF_CONST"))) constant;
> +    struct ipa_pass_through_data GTY ((tag ("IPA_JF_PASS_THROUGH"))) pass_through;
> +    struct ipa_load_agg_data GTY ((tag ("IPA_JF_LOAD_AGG"))) load_agg;
> +  } GTY ((desc ("%1.jftype"))) value;
> +};
>  
> -/* Aggregate jump function - i.e. description of contents of aggregates passed
> -   either by reference or value.  */
> +/* Jump functions describing a set of aggregate contents.  */
>  
>  struct GTY(()) ipa_agg_jump_function
>  {
> -  /* Description of the individual items.  */
> +  /* Description of the individual jump function item.  */
>    vec<ipa_agg_jf_item, va_gc> *items;
> -  /* True if the data was passed by reference (as opposed to by value). */
> +  /* True if the data was passed by reference (as opposed to by value).  */
>    bool by_ref;
> +};
> +
> +/* An element in an aggregate part describing a known value at a given offset.
> +   All unlisted positions are assumed to be unknown and all listed values must
> +   fulfill is_gimple_ip_invariant.  */
> +
> +struct GTY(()) ipa_agg_value

Why the GTY marker, is this structure ever allocated in garbage
collected memory?  I don't think so (but it is getting late here).

> +{
> +  /* The offset at which the known value is located within the aggregate.  */
> +  HOST_WIDE_INT offset;
>  
> -  /* Return true if OTHER describes same agg items.  */
> -  bool equal_to (const ipa_agg_jump_function &other)
> +  /* The known constant.  */
> +  tree value;
> +
> +  /* Return true if OTHER describes same agg value.  */
> +  bool equal_to (const ipa_agg_value &other);
> +};
> +
> +/* Structure describing a set of known offset/value for aggregate.  */
> +
> +struct GTY(()) ipa_agg_value_set

Likewise, moreover...

> +{
> +  /* Description of the individual item.  */
> +  vec<ipa_agg_value> items;

...if it is, this vector will not be handled by GC well.

> +  /* True if the data was passed by reference (as opposed to by value).  */
> +  bool by_ref;
> +
> +  /* Return true if OTHER describes same agg values.  */
> +  bool equal_to (const ipa_agg_value_set &other)
>    {
>      if (by_ref != other.by_ref)
>        return false;
> -    if (items != NULL && other.items == NULL)
> -      return false;
> -    if (!items)
> -      return other.items == NULL;
> -    if (items->length () != other.items->length ())
> +    if (items.length () != other.items.length ())
>        return false;
> -    for (unsigned int i = 0; i < items->length (); i++)
> -      if (!(*items)[i].equal_to ((*other.items)[i]))
> +    for (unsigned int i = 0; i < items.length (); i++)
> +      if (!items[i].equal_to (other.items[i]))
>  	return false;
>      return true;
>    }
> +
> +  /* Return true if there is any value for aggregate.  */
> +  operator bool () const
> +  {
> +    return !items.is_empty ();
> +  }

I do not know various C++ conventions well, but unless this is a really
really well established one, please don't use an operator but a normal
method.  (My preference is to invert its meaning and call it is_empty
:-)

> +
> +  ipa_agg_value_set copy () const
> +  {
> +    ipa_agg_value_set new_copy;
> +
> +    new_copy.items = items.copy ();
> +    new_copy.by_ref = by_ref;
> +
> +    return new_copy;
> +  }
> +
> +  void release ()
> +  {
> +    items.release ();
> +  }
>  };
>  

Thanks!

Martin
Feng Xue OS Nov. 5, 2019, 9:35 a.m. UTC | #2
Hi Martin,

  Thanks for your review. I updated the patch with your comments.

Feng

----

> Sorry that it took so long.  Next time, please consider making the
> review a bit easier by writing a ChangeLog (yes, I usually read them and
> you'll have to write one anyway).
>> +      class ipcp_param_lattices *src_plats
>> +             = ipa_get_parm_lattices (info, src_idx);

> Wrong indentation for GNU coding standard.
Done.

>>  struct ipa_known_agg_contents_list
>>  {
>>    /* Offset and size of the described part of the aggregate.  */
>>    HOST_WIDE_INT offset, size;
>> -  /* Known constant value or NULL if the contents is known to be unknown.  */
>> -  tree constant;
>> +
>> +  /* Type of the described part of the aggregate.  */
>> +  tree type;
>> +
>> +  /* Known constant value or jump function data describing contents.  */
>> +  struct ipa_load_agg_data value;

> I wonder whether it would be cleaner to repeat the fields of
> ipa_load_agg_dat here.  But I don't insist.
But this will make duplicate code. If we want to add more information to
describe agg jump function, we have to copy that here.

>> +         item.value.pass_through.operand
>> +                             = unshare_expr_without_location (operand);

> Wrong indentation for GNU coding standard.
Done.

>> +   Since load-value-from-aggregate jump function data structure is informative
>> +   enough to describe constant and simple pass-through jump function, here we
>> +   do not need a jump function type, merely use FORMAL_ID and OPERAND in
>> +   IPA_LOAD_AGG_DATA to distinguish different jump functions.  */

> This last comment is difficult to understand to the point when IMHO one
> has to read the code anyway.  Perhaps you could just list which special
> values imply which final jump function type?  And perhaps that list
> should go to the comment describing ipa_known_agg_contents_list.
Changed the comments.

>> +
>> +static void
>> +compute_assign_agg_jump_func (struct ipa_func_body_info *fbi,
>> +                           struct ipa_load_agg_data *agg_value,

> My preference would be for this function to receive a pointer to the
> whole ipa_known_agg_contents_list as a parameter instead of agg_value,
> and to be called something like extract_agg_content_or_origin or
> something that would not suggest it creates a real jump function.
> Please at least consider changing the name.
I deliberately split this function from extract_mem_content, and limits
its access scope to part of ipa_known_agg_contents_list, not whole,
since it only analyzes rhs side of assignment to an aggregate.

Rename to analyze_agg_content_value.

>> +      if (!is_gimple_assign (stmt = SSA_NAME_DEF_STMT (rhs1)))

> Please put the assignment into a separate statement.
Done.

>>  /* Traverse statements from CALL backwards, scanning whether an aggregate given
>> -   in ARG is filled in with constant values.  ARG can either be an aggregate
>> -   expression or a pointer to an aggregate.  ARG_TYPE is the type of the
>> -   aggregate.  JFUNC is the jump function into which the constants are
>> -   subsequently stored.  AA_WALK_BUDGET_P points to limit on number of
>> -   statements we allow get_continuation_for_phi to examine.  */
>> +   in ARG is filled in constant or value that is derived from caller's formal

> Minor nit, please use plural "constants or values that are..."
Done.

>> -       if (src->agg.items
>> -           && (dst->value.ancestor.agg_preserved || !src->agg.by_ref))
>> +       /* Parameter and argument in ancestor jump function must be pointer
>> +          type, which means access to aggregate must be by-reference.  */
>> +       gcc_checking_assert (!src->agg.items || src->agg.by_ref);

> I am slightly afraid that some type mismatches in between the call
> statement fntype and callee type which are possible with LTO (and
> horribly bad user input) might trigger this.  Please make this a
> non-checking assert so that we find out if that is indeed true.
Done.

>> +struct GTY(()) ipa_agg_value

> Why the GTY marker, is this structure ever allocated in garbage
> collected memory?  I don't think so (but it is getting late here).
Removed GTY marker.

>> +struct GTY(()) ipa_agg_value_set

> Likewise, moreover...
Removed.

>> +  /* Return true if there is any value for aggregate.  */
>> +  operator bool () const
>> +  {
>> +    return !items.is_empty ();
>> +  }

> I do not know various C++ conventions well, but unless this is a really
> really well established one, please don't use an operator but a normal
> method.  (My preference is to invert its meaning and call it is_empty
Done.
Jan Hubicka Nov. 12, 2019, 12:15 p.m. UTC | #3
> +2019-11-05  Feng Xue  <fxue@os.amperecomputing.com>
> +
> +	PR ipa/91682
> +	* ipa-prop.h (jump_func_type): New value IPA_JF_LOAD_AGG.
> +	(ipa_load_agg_data, ipa_agg_value, ipa_agg_value_set): New structs.
> +	(ipa_agg_jf_item): Add new field jftype and type, redefine field value.
> +	(ipa_agg_jump_function): Remove member function equal_to.
> +	(ipa_agg_jump_function_p): Remove typedef.
> +	(ipa_copy_agg_values, ipa_release_agg_values): New functions.
> +	* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Dump
> +	information for aggregate jump function.
> +	(get_ssa_def_if_simple_copy): Add new parameter rhs_stmt to
> +	record last definition statement.
> +	(load_from_unmodified_param_or_agg): New function.
> +	(ipa_known_agg_contents_list): Add new field type and value, remove
> +	field constant.
> +	(build_agg_jump_func_from_list): Rename parameter const_count to
> +	value_count, build aggregate jump function from ipa_load_agg_data.
> +	(analyze_agg_content_value): New function.
> +	(extract_mem_content): Analyze memory store assignment to prepare
> +	information for aggregate jump function generation.
> +	(determine_known_aggregate_parts): Add new parameter fbi, remove
> +	parameter aa_walk_budeget_p.
> +	(update_jump_functions_after_inlining): Update aggregate jump function.
> +	(ipa_find_agg_cst_for_param): Change type of parameter agg.
> +	(try_make_edge_direct_simple_call): Add new parameter new_root.
> +	(try_make_edge_direct_virtual_call): Add new parameter new_root and
> +	new_root_info.
> +	(update_indirect_edges_after_inlining): Pass new argument to
> +	try_make_edge_direct_simple_call and try_make_edge_direct_virtual_call.
> +	(ipa_write_jump_function): Write aggregate jump function to file.
> +	(ipa_read_jump_function): Read aggregate jump function from file.
> +	(ipa_agg_value::equal_to): Migrate from ipa_agg_jf_item::equal_to.
> +	* ipa-cp.c (ipa_get_jf_arith_result): New function.
> +	(ipa_agg_value_from_node): Likewise.
> +	(ipa_agg_value_set_from_jfunc): Likewise.
> +	(propagate_vals_across_arith_jfunc): Likewise.
> +	(propagate_aggregate_lattice): Likewise.
> +	(ipa_get_jf_pass_through_result): Call ipa_get_jf_arith_result.
> +	(propagate_vals_across_pass_through): Call
> +	propagate_vals_across_arith_jfunc.
> +	(get_clone_agg_value): Move forward.
> +	(propagate_aggs_across_jump_function): Handle aggregate jump function
> +	propagation.
> +	(agg_jmp_p_vec_for_t_vec): Remove.
> +	(context_independent_aggregate_values): Change use of
> +	vec<ipa_agg_jf_item> to vec<ipa_agg_value>.
> +	(copy_plats_to_inter, intersect_with_plats): Likewise.
> +	(agg_replacements_to_vector, intersect_with_agg_replacements): Likewise.
> +	(intersect_aggregate_with_edge): Likewise.
> +	(find_aggregate_values_for_callers_subset): Likewise.
> +	(cgraph_edge_brings_all_agg_vals_for_node): Likewise.
> +	(estimate_local_effects): Change use of vec<ipa_agg_jump_function>
> +	and vec<ipa_agg_jump_function_p> to vec<ipa_agg_value_set>.
> +	(gather_context_independent_values): Likewise.
> +	(perform_estimation_of_a_value, decide_whether_version_node): Likewise.
> +	* ipa-fnsummary.c (evaluate_conditions_for_known_args): Change use of
> +	vec<ipa_agg_jump_function_p> to vec<ipa_agg_value_set>.
> +	(evaluate_properties_for_edge): Likewise.
> +	(estimate_edge_devirt_benefit): Likewise.
> +	(estimate_edge_size_and_time):  Likewise.
> +	(estimate_calls_size_and_time): Likewise.
> +	(ipa_call_context::ipa_call_context): Likewise.
> +	(estimate_ipcp_clone_size_and_time):  Likewise.
> +	* ipa-fnsummary.h (ipa_call_context): Change use of
> +	vec<ipa_agg_jump_function_p> to vec<ipa_agg_value_set>.
> +	* ipa-inline-analysis.c (do_estimate_edge_time): Change use of
> +	vec<ipa_agg_jump_function_p> to vec<ipa_agg_value_set>.
> +	(do_estimate_edge_size): Likewise.
> +	(do_estimate_edge_hints): Likewise.
> +

OK, thanks - this looks like very nice ipa-prop improvement.

Honza
Jan Hubicka Nov. 12, 2019, 12:34 p.m. UTC | #4
> > +2019-11-05  Feng Xue  <fxue@os.amperecomputing.com>
> > +
> > +	PR ipa/91682
> > +	* ipa-prop.h (jump_func_type): New value IPA_JF_LOAD_AGG.
> > +	(ipa_load_agg_data, ipa_agg_value, ipa_agg_value_set): New structs.
> > +	(ipa_agg_jf_item): Add new field jftype and type, redefine field value.
> > +	(ipa_agg_jump_function): Remove member function equal_to.
> > +	(ipa_agg_jump_function_p): Remove typedef.
> > +	(ipa_copy_agg_values, ipa_release_agg_values): New functions.
> > +	* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Dump
> > +	information for aggregate jump function.
> > +	(get_ssa_def_if_simple_copy): Add new parameter rhs_stmt to
> > +	record last definition statement.
> > +	(load_from_unmodified_param_or_agg): New function.
> > +	(ipa_known_agg_contents_list): Add new field type and value, remove
> > +	field constant.
> > +	(build_agg_jump_func_from_list): Rename parameter const_count to
> > +	value_count, build aggregate jump function from ipa_load_agg_data.
> > +	(analyze_agg_content_value): New function.
> > +	(extract_mem_content): Analyze memory store assignment to prepare
> > +	information for aggregate jump function generation.
> > +	(determine_known_aggregate_parts): Add new parameter fbi, remove
> > +	parameter aa_walk_budeget_p.
> > +	(update_jump_functions_after_inlining): Update aggregate jump function.
> > +	(ipa_find_agg_cst_for_param): Change type of parameter agg.
> > +	(try_make_edge_direct_simple_call): Add new parameter new_root.
> > +	(try_make_edge_direct_virtual_call): Add new parameter new_root and
> > +	new_root_info.
> > +	(update_indirect_edges_after_inlining): Pass new argument to
> > +	try_make_edge_direct_simple_call and try_make_edge_direct_virtual_call.
> > +	(ipa_write_jump_function): Write aggregate jump function to file.
> > +	(ipa_read_jump_function): Read aggregate jump function from file.
> > +	(ipa_agg_value::equal_to): Migrate from ipa_agg_jf_item::equal_to.
> > +	* ipa-cp.c (ipa_get_jf_arith_result): New function.
> > +	(ipa_agg_value_from_node): Likewise.
> > +	(ipa_agg_value_set_from_jfunc): Likewise.
> > +	(propagate_vals_across_arith_jfunc): Likewise.
> > +	(propagate_aggregate_lattice): Likewise.
> > +	(ipa_get_jf_pass_through_result): Call ipa_get_jf_arith_result.
> > +	(propagate_vals_across_pass_through): Call
> > +	propagate_vals_across_arith_jfunc.
> > +	(get_clone_agg_value): Move forward.
> > +	(propagate_aggs_across_jump_function): Handle aggregate jump function
> > +	propagation.
> > +	(agg_jmp_p_vec_for_t_vec): Remove.
> > +	(context_independent_aggregate_values): Change use of
> > +	vec<ipa_agg_jf_item> to vec<ipa_agg_value>.
> > +	(copy_plats_to_inter, intersect_with_plats): Likewise.
> > +	(agg_replacements_to_vector, intersect_with_agg_replacements): Likewise.
> > +	(intersect_aggregate_with_edge): Likewise.
> > +	(find_aggregate_values_for_callers_subset): Likewise.
> > +	(cgraph_edge_brings_all_agg_vals_for_node): Likewise.
> > +	(estimate_local_effects): Change use of vec<ipa_agg_jump_function>
> > +	and vec<ipa_agg_jump_function_p> to vec<ipa_agg_value_set>.
> > +	(gather_context_independent_values): Likewise.
> > +	(perform_estimation_of_a_value, decide_whether_version_node): Likewise.
> > +	* ipa-fnsummary.c (evaluate_conditions_for_known_args): Change use of
> > +	vec<ipa_agg_jump_function_p> to vec<ipa_agg_value_set>.
> > +	(evaluate_properties_for_edge): Likewise.
> > +	(estimate_edge_devirt_benefit): Likewise.
> > +	(estimate_edge_size_and_time):  Likewise.
> > +	(estimate_calls_size_and_time): Likewise.
> > +	(ipa_call_context::ipa_call_context): Likewise.
> > +	(estimate_ipcp_clone_size_and_time):  Likewise.
> > +	* ipa-fnsummary.h (ipa_call_context): Change use of
> > +	vec<ipa_agg_jump_function_p> to vec<ipa_agg_value_set>.
> > +	* ipa-inline-analysis.c (do_estimate_edge_time): Change use of
> > +	vec<ipa_agg_jump_function_p> to vec<ipa_agg_value_set>.
> > +	(do_estimate_edge_size): Likewise.
> > +	(do_estimate_edge_hints): Likewise.
> > +
> 
> OK, thanks - this looks like very nice ipa-prop improvement.
Also note that there is a long standing problem with inlining ipacp
clones.  This can be shown on the following example:

struct a {int a;};
static int foo (struct a a)
{
  return a.a;
}
__attribute__ ((noinline))
static int bar (struct a a)
{
  return foo(a);
}
main()
{
  struct a a={1};
  return bar (a);
}

Now if you compile it with -O2 -fno-early-inlining ipacp correctly
determines constants:

Estimating effects for bar/1.
   Estimating body: bar/1
   Known to be false: 
   size:6 time:14.000000 nonspec time:14.000000
 - context independent values, size: 6, time_benefit: 0.000000
     Decided to specialize for all known contexts, code not going to grow.
Setting dest_lattice to bottom, because type of param 0 of foo is NULL or unsuitable for bits propagation

Estimating effects for foo/0.
   Estimating body: foo/0
   Known to be false: op0[offset: 0] changed
   size:3 time:2.000000 nonspec time:3.000000
 - context independent values, size: 3, time_benefit: 1.000000
     Decided to specialize for all known contexts, code not going to grow.


Yet the intended tranformation to "return 1" does not happen:

__attribute__((noinline))
bar.constprop (struct a a)
{
  int a$a;

  <bb 2> [local count: 1073741824]:
  a$a_5 = a.a;
  return a$a_5;

}



;; Function main (main, funcdef_no=2, decl_uid=1937, cgraph_uid=3, symbol_order=2) (executed once)

main ()
{
  struct a a;
  int _3;

  <bb 2> [local count: 1073741824]:
  a.a = 1;
  _3 = bar.constprop (a); [tail call]
  a ={v} {CLOBBER};
  return _3;

}

The problem here is that foo get inlined into bar and we never apply
ipcp transform on foo, so a.a never gets constant propagated.  

For value ranges this works since late passes are able to propagate
constants from value ranges we attach to the default def SSA names.  I
think correct answer here is to do no subtitution in in ipa-prop.c
transform function.  Rather note the known values for late passes and
let FRE do its job.

Honza
Martin Jambor Nov. 12, 2019, 1:15 p.m. UTC | #5
Hi,

On Tue, Nov 12 2019, Jan Hubicka wrote:
> Also note that there is a long standing problem with inlining ipacp
> clones.  This can be shown on the following example:
>
> struct a {int a;};
> static int foo (struct a a)
> {
>   return a.a;
> }
> __attribute__ ((noinline))
> static int bar (struct a a)
> {
>   return foo(a);
> }
> main()
> {
>   struct a a={1};
>   return bar (a);
> }
>
> Now if you compile it with -O2 -fno-early-inlining ipacp correctly
> determines constants:
>
> Estimating effects for bar/1.
>    Estimating body: bar/1
>    Known to be false: 
>    size:6 time:14.000000 nonspec time:14.000000
>  - context independent values, size: 6, time_benefit: 0.000000
>      Decided to specialize for all known contexts, code not going to grow.
> Setting dest_lattice to bottom, because type of param 0 of foo is NULL or unsuitable for bits propagation
>
> Estimating effects for foo/0.
>    Estimating body: foo/0
>    Known to be false: op0[offset: 0] changed
>    size:3 time:2.000000 nonspec time:3.000000
>  - context independent values, size: 3, time_benefit: 1.000000
>      Decided to specialize for all known contexts, code not going to grow.
>
>
> Yet the intended tranformation to "return 1" does not happen:
>
> __attribute__((noinline))
> bar.constprop (struct a a)
> {
>   int a$a;
>
>   <bb 2> [local count: 1073741824]:
>   a$a_5 = a.a;
>   return a$a_5;
>
> }
>
>
>
> ;; Function main (main, funcdef_no=2, decl_uid=1937, cgraph_uid=3, symbol_order=2) (executed once)
>
> main ()
> {
>   struct a a;
>   int _3;
>
>   <bb 2> [local count: 1073741824]:
>   a.a = 1;
>   _3 = bar.constprop (a); [tail call]
>   a ={v} {CLOBBER};
>   return _3;
>
> }
>
> The problem here is that foo get inlined into bar and we never apply
> ipcp transform on foo, so a.a never gets constant propagated.  

Ugh, we never... what?  That is quite bad, how come we don't have PR
about this?

>
> For value ranges this works since late passes are able to propagate
> constants from value ranges we attach to the default def SSA names.  I

Well, there are no SSA names for parts of aggregates.

> think correct answer here is to do no subtitution in in ipa-prop.c
> transform function.  Rather note the known values for late passes and
> let FRE do its job.

And where would you like to save it?   Do a load at the beginning of the
function?  My thinking was that it is better to modify the IL rather
than storing stuff to ad-hoc on-the-side data structures.

Martin
Jan Hubicka Nov. 12, 2019, 1:27 p.m. UTC | #6
> Hi,
> 
> On Tue, Nov 12 2019, Jan Hubicka wrote:
> > Also note that there is a long standing problem with inlining ipacp
> > clones.  This can be shown on the following example:
> >
> > struct a {int a;};
> > static int foo (struct a a)
> > {
> >   return a.a;
> > }
> > __attribute__ ((noinline))
> > static int bar (struct a a)
> > {
> >   return foo(a);
> > }
> > main()
> > {
> >   struct a a={1};
> >   return bar (a);
> > }
> >
> > Now if you compile it with -O2 -fno-early-inlining ipacp correctly
> > determines constants:
> >
> > Estimating effects for bar/1.
> >    Estimating body: bar/1
> >    Known to be false: 
> >    size:6 time:14.000000 nonspec time:14.000000
> >  - context independent values, size: 6, time_benefit: 0.000000
> >      Decided to specialize for all known contexts, code not going to grow.
> > Setting dest_lattice to bottom, because type of param 0 of foo is NULL or unsuitable for bits propagation
> >
> > Estimating effects for foo/0.
> >    Estimating body: foo/0
> >    Known to be false: op0[offset: 0] changed
> >    size:3 time:2.000000 nonspec time:3.000000
> >  - context independent values, size: 3, time_benefit: 1.000000
> >      Decided to specialize for all known contexts, code not going to grow.
> >
> >
> > Yet the intended tranformation to "return 1" does not happen:
> >
> > __attribute__((noinline))
> > bar.constprop (struct a a)
> > {
> >   int a$a;
> >
> >   <bb 2> [local count: 1073741824]:
> >   a$a_5 = a.a;
> >   return a$a_5;
> >
> > }
> >
> >
> >
> > ;; Function main (main, funcdef_no=2, decl_uid=1937, cgraph_uid=3, symbol_order=2) (executed once)
> >
> > main ()
> > {
> >   struct a a;
> >   int _3;
> >
> >   <bb 2> [local count: 1073741824]:
> >   a.a = 1;
> >   _3 = bar.constprop (a); [tail call]
> >   a ={v} {CLOBBER};
> >   return _3;
> >
> > }
> >
> > The problem here is that foo get inlined into bar and we never apply
> > ipcp transform on foo, so a.a never gets constant propagated.  
> 
> Ugh, we never... what?  That is quite bad, how come we don't have PR
> about this?

I remember speaking about it with you few times years ago :)
> 
> >
> > For value ranges this works since late passes are able to propagate
> > constants from value ranges we attach to the default def SSA names.  I
> 
> Well, there are no SSA names for parts of aggregates.

I think all we need is to make FRE's alias oracle walker which is
responsible for propagation of constants to see if it hits entry of
function, check that base is a parameter and look into ipcp transform
summary if known value is there.
> 
> > think correct answer here is to do no subtitution in in ipa-prop.c
> > transform function.  Rather note the known values for late passes and
> > let FRE do its job.
> 
> And where would you like to save it?   Do a load at the beginning of the
> function?  My thinking was that it is better to modify the IL rather
> than storing stuff to ad-hoc on-the-side data structures.

It is already saved in the ipcp transform summary. It is about keeping
it around while copmiling function and using it same way as we use, say
results of ipa-reference analysis.

Honza
> 
> Martin
Feng Xue OS Nov. 13, 2019, 10:14 a.m. UTC | #7
Thanks. 

And for this issue, we can add a new tracker as a followup task.

Feng
Feng Xue OS Nov. 13, 2019, 10:18 a.m. UTC | #8
Please check the attachment, and this patch is based on the previous extended agg-jump-function patch.

Thanks,
Feng
Richard Biener Nov. 13, 2019, 1:47 p.m. UTC | #9
On Tue, Nov 12, 2019 at 2:27 PM Jan Hubicka <hubicka@ucw.cz> wrote:
>
> > Hi,
> >
> > On Tue, Nov 12 2019, Jan Hubicka wrote:
> > > Also note that there is a long standing problem with inlining ipacp
> > > clones.  This can be shown on the following example:
> > >
> > > struct a {int a;};
> > > static int foo (struct a a)
> > > {
> > >   return a.a;
> > > }
> > > __attribute__ ((noinline))
> > > static int bar (struct a a)
> > > {
> > >   return foo(a);
> > > }
> > > main()
> > > {
> > >   struct a a={1};
> > >   return bar (a);
> > > }
> > >
> > > Now if you compile it with -O2 -fno-early-inlining ipacp correctly
> > > determines constants:
> > >
> > > Estimating effects for bar/1.
> > >    Estimating body: bar/1
> > >    Known to be false:
> > >    size:6 time:14.000000 nonspec time:14.000000
> > >  - context independent values, size: 6, time_benefit: 0.000000
> > >      Decided to specialize for all known contexts, code not going to grow.
> > > Setting dest_lattice to bottom, because type of param 0 of foo is NULL or unsuitable for bits propagation
> > >
> > > Estimating effects for foo/0.
> > >    Estimating body: foo/0
> > >    Known to be false: op0[offset: 0] changed
> > >    size:3 time:2.000000 nonspec time:3.000000
> > >  - context independent values, size: 3, time_benefit: 1.000000
> > >      Decided to specialize for all known contexts, code not going to grow.
> > >
> > >
> > > Yet the intended tranformation to "return 1" does not happen:
> > >
> > > __attribute__((noinline))
> > > bar.constprop (struct a a)
> > > {
> > >   int a$a;
> > >
> > >   <bb 2> [local count: 1073741824]:
> > >   a$a_5 = a.a;
> > >   return a$a_5;
> > >
> > > }
> > >
> > >
> > >
> > > ;; Function main (main, funcdef_no=2, decl_uid=1937, cgraph_uid=3, symbol_order=2) (executed once)
> > >
> > > main ()
> > > {
> > >   struct a a;
> > >   int _3;
> > >
> > >   <bb 2> [local count: 1073741824]:
> > >   a.a = 1;
> > >   _3 = bar.constprop (a); [tail call]
> > >   a ={v} {CLOBBER};
> > >   return _3;
> > >
> > > }
> > >
> > > The problem here is that foo get inlined into bar and we never apply
> > > ipcp transform on foo, so a.a never gets constant propagated.
> >
> > Ugh, we never... what?  That is quite bad, how come we don't have PR
> > about this?
>
> I remember speaking about it with you few times years ago :)
> >
> > >
> > > For value ranges this works since late passes are able to propagate
> > > constants from value ranges we attach to the default def SSA names.  I
> >
> > Well, there are no SSA names for parts of aggregates.
>
> I think all we need is to make FRE's alias oracle walker which is
> responsible for propagation of constants to see if it hits entry of
> function, check that base is a parameter and look into ipcp transform
> summary if known value is there.

Why don't we instantiate IPA CP clones we want to inline and then
inline the instantiated function instead of the original?  Isn't that simpler
and not too bad since it shouldn't happen all so often?

Or apply the IPA transform during inlining itself...

> > > think correct answer here is to do no subtitution in in ipa-prop.c
> > > transform function.  Rather note the known values for late passes and
> > > let FRE do its job.
> >
> > And where would you like to save it?   Do a load at the beginning of the
> > function?  My thinking was that it is better to modify the IL rather
> > than storing stuff to ad-hoc on-the-side data structures.
>
> It is already saved in the ipcp transform summary. It is about keeping
> it around while copmiling function and using it same way as we use, say
> results of ipa-reference analysis.
>
> Honza
> >
> > Martin
Jan Hubicka Nov. 13, 2019, 2:39 p.m. UTC | #10
> On Tue, Nov 12, 2019 at 2:27 PM Jan Hubicka <hubicka@ucw.cz> wrote:
> >
> > > Hi,
> > >
> > > On Tue, Nov 12 2019, Jan Hubicka wrote:
> > > > Also note that there is a long standing problem with inlining ipacp
> > > > clones.  This can be shown on the following example:
> > > >
> > > > struct a {int a;};
> > > > static int foo (struct a a)
> > > > {
> > > >   return a.a;
> > > > }
> > > > __attribute__ ((noinline))
> > > > static int bar (struct a a)
> > > > {
> > > >   return foo(a);
> > > > }
> > > > main()
> > > > {
> > > >   struct a a={1};
> > > >   return bar (a);
> > > > }
> > > >
> > > > Now if you compile it with -O2 -fno-early-inlining ipacp correctly
> > > > determines constants:
> > > >
> > > > Estimating effects for bar/1.
> > > >    Estimating body: bar/1
> > > >    Known to be false:
> > > >    size:6 time:14.000000 nonspec time:14.000000
> > > >  - context independent values, size: 6, time_benefit: 0.000000
> > > >      Decided to specialize for all known contexts, code not going to grow.
> > > > Setting dest_lattice to bottom, because type of param 0 of foo is NULL or unsuitable for bits propagation
> > > >
> > > > Estimating effects for foo/0.
> > > >    Estimating body: foo/0
> > > >    Known to be false: op0[offset: 0] changed
> > > >    size:3 time:2.000000 nonspec time:3.000000
> > > >  - context independent values, size: 3, time_benefit: 1.000000
> > > >      Decided to specialize for all known contexts, code not going to grow.
> > > >
> > > >
> > > > Yet the intended tranformation to "return 1" does not happen:
> > > >
> > > > __attribute__((noinline))
> > > > bar.constprop (struct a a)
> > > > {
> > > >   int a$a;
> > > >
> > > >   <bb 2> [local count: 1073741824]:
> > > >   a$a_5 = a.a;
> > > >   return a$a_5;
> > > >
> > > > }
> > > >
> > > >
> > > >
> > > > ;; Function main (main, funcdef_no=2, decl_uid=1937, cgraph_uid=3, symbol_order=2) (executed once)
> > > >
> > > > main ()
> > > > {
> > > >   struct a a;
> > > >   int _3;
> > > >
> > > >   <bb 2> [local count: 1073741824]:
> > > >   a.a = 1;
> > > >   _3 = bar.constprop (a); [tail call]
> > > >   a ={v} {CLOBBER};
> > > >   return _3;
> > > >
> > > > }
> > > >
> > > > The problem here is that foo get inlined into bar and we never apply
> > > > ipcp transform on foo, so a.a never gets constant propagated.
> > >
> > > Ugh, we never... what?  That is quite bad, how come we don't have PR
> > > about this?
> >
> > I remember speaking about it with you few times years ago :)
> > >
> > > >
> > > > For value ranges this works since late passes are able to propagate
> > > > constants from value ranges we attach to the default def SSA names.  I
> > >
> > > Well, there are no SSA names for parts of aggregates.
> >
> > I think all we need is to make FRE's alias oracle walker which is
> > responsible for propagation of constants to see if it hits entry of
> > function, check that base is a parameter and look into ipcp transform
> > summary if known value is there.
> 
> Why don't we instantiate IPA CP clones we want to inline and then
> inline the instantiated function instead of the original?  Isn't that simpler

That is what we discussed originally ;)

One problem is that as the code is organized currently applying tranform
to a function will apply inline decisions. Inline decisions may differ
in inline and offline version of the code.

This can be fixed by arranging one additional offline copy (so having
possibly two) which never gets inlined and call ipcp transform on it
specially.

However this does not solve problem fully - if I extended the testcase
so the access goes thorugh pointer which becomes &a only during late
optimization, we will still have missed optimization.

The way value ranges happens to work by letting local optimizations to
do the propagation into inline copies seems quite easy and robust.
It seems to me that it is easiest to maintain variant here - think of
ipcp as an analysis pass which does the propagation (and decides on
cloning) and let late passes to update gimple bodies.
> and not too bad since it shouldn't happen all so often?
> 
> Or apply the IPA transform during inlining itself...

Yep, that was other solution I was thinking about - it would be nice to
not make tree-inline more complex though.

Honza
Martin Jambor Nov. 13, 2019, 2:47 p.m. UTC | #11
On Wed, Nov 13 2019, Feng Xue OS wrote:
> Thanks. 
>
> And for this issue, we can add a new tracker as a followup task.

It's now PR 92497.

Thanks,

Martin


>
> Feng
>
> ________________________________________
> From: Jan Hubicka <hubicka@ucw.cz>
> Sent: Tuesday, November 12, 2019 8:34 PM
> To: Feng Xue OS
> Cc: Martin Jambor; gcc-patches@gcc.gnu.org
> Subject: Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
>
>>
>> OK, thanks - this looks like very nice ipa-prop improvement.
> Also note that there is a long standing problem with inlining ipacp
> clones.  This can be shown on the following example:
>
> struct a {int a;};
> static int foo (struct a a)
> {
>   return a.a;
> }
> __attribute__ ((noinline))
> static int bar (struct a a)
> {
>   return foo(a);
> }
> main()
> {
>   struct a a={1};
>   return bar (a);
> }
>
> Now if you compile it with -O2 -fno-early-inlining ipacp correctly
> determines constants:
>
> Estimating effects for bar/1.
>    Estimating body: bar/1
>    Known to be false:
>    size:6 time:14.000000 nonspec time:14.000000
>  - context independent values, size: 6, time_benefit: 0.000000
>      Decided to specialize for all known contexts, code not going to grow.
> Setting dest_lattice to bottom, because type of param 0 of foo is NULL or unsuitable for bits propagation
>
> Estimating effects for foo/0.
>    Estimating body: foo/0
>    Known to be false: op0[offset: 0] changed
>    size:3 time:2.000000 nonspec time:3.000000
>  - context independent values, size: 3, time_benefit: 1.000000
>      Decided to specialize for all known contexts, code not going to grow.
>
>
> Yet the intended tranformation to "return 1" does not happen:
>
> __attribute__((noinline))
> bar.constprop (struct a a)
> {
>   int a$a;
>
>   <bb 2> [local count: 1073741824]:
>   a$a_5 = a.a;
>   return a$a_5;
>
> }
>
>
>
> ;; Function main (main, funcdef_no=2, decl_uid=1937, cgraph_uid=3, symbol_order=2) (executed once)
>
> main ()
> {
>   struct a a;
>   int _3;
>
>   <bb 2> [local count: 1073741824]:
>   a.a = 1;
>   _3 = bar.constprop (a); [tail call]
>   a ={v} {CLOBBER};
>   return _3;
>
> }
>
> The problem here is that foo get inlined into bar and we never apply
> ipcp transform on foo, so a.a never gets constant propagated.
>
> For value ranges this works since late passes are able to propagate
> constants from value ranges we attach to the default def SSA names.  I
> think correct answer here is to do no subtitution in in ipa-prop.c
> transform function.  Rather note the known values for late passes and
> let FRE do its job.
>
> Honza
Jan Hubicka Nov. 15, 2019, 8:09 a.m. UTC | #12
Hi,
building Firefox I now get:

#0  internal_error (gmsgid=0x28f8597 "in %s, at %s:%d") at ../../gcc/diagnostic.c:1578
#1  0x0000000001e20ae2 in fancy_abort (file=0x2892574 "../../gcc/ipa-cp.c", line=386, function=0x2
    893510 <ipa_get_parm_lattices(ipa_node_params*, int)::__FUNCTION__> "ipa_get_parm_lattices") at ../../gcc/diagnostic.c:1649
#2  0x0000000001cf42cd in ipa_get_parm_lattices (info=0x7fff7e4de360, i=3) at ../../gcc/ipa-cp.c:386
#3  0x0000000001cf6aa1 in ipa_agg_value_from_node (info=0x7fff7e4de360, node=0x7fff9bf1b000, item=0x7f
    ff97e1cb08) at ../../gcc/ipa-cp.c:1612
#4  0x0000000001cf6c8c in ipa_agg_value_set_from_jfunc (info=0x7fff7e4de360, node=0x7fff9bf1b000, ag
    g_jfunc=0x7fff32b15a60) at ../../gcc/ipa-cp.c:1682
#5  0x0000000000c84fcb in evaluate_properties_for_edge (e=0x7fff2ceac068, inline_p=true, clause_pt
    r=0x7fffffffde04, nonspec_clause_ptr=0x7fffffffde00, known_vals_ptr=0x7fffffffddf8, known_con
    texts_ptr=0x7fffffffddf0, known_aggs_ptr=0x7fffffffdde8) at ../../gcc/ipa-fnsummary.c:573
#6  0x0000000000ca4d6a in do_estimate_edge_time (edge=0x7fff2ceac068) at ../../gcc/ipa-inline-analysis.c:200
#7  0x0000000000ca52cf in do_estimate_edge_size (edge=0x7fff2ceac068) at ../../gcc/ipa-inline-analysis.c:317
#8  0x0000000001d0e9d8 in estimate_edge_size (edge=0x7fff2ceac068) at ../../gcc/ipa-inline.h:78
#9  0x0000000001d0ea28 in estimate_edge_growth (edge=0x7fff2ceac068) at ../../gcc/ipa-inline.h:89
#10 0x0000000001d10d0c in want_inline_small_function_p (e=0x7fff2ceac068, report=false) at ../../gcc/ipa-inline.c:84
   4
#11 0x0000000001d12bbb in update_callee_keys (heap=0x7fffffffe0f0, node=0x7fff9bf1b000, updated_
    nodes=0x7fffffffe0d0) at ../../gcc/ipa-inline.c:1470
#12 0x0000000001d14bca in inline_small_functions () at ../../gcc/ipa-inline.c:2153
#13 0x0000000001d165d8 in ipa_inline () at ../../gcc/ipa-inline.c:2613
#14 0x0000000001d17353 in (anonymous namespace)::pass_ipa_inline::execute (this=0x2f111e0) at ../../gcc/ipa-inline.c:3013
#15 0x0000000000e6c5a5 in execute_one_pass (pass=0x2f111e0) at ../../gcc/passes.c:2494
#16 0x0000000000e6d4ee in execute_ipa_pass_list (pass=0x2f111e0) at ../../gcc/passes.c:2921
#17 0x00000000008c6f19 in do_whole_program_analysis () at ../../gcc/lto/lto.c:469
#18 0x00000000008c721f in lto_main () at ../../gcc/lto/lto.c:641
#19 0x0000000000fbcc5b in compile_file () at ../../gcc/toplev.c:458
#20 0x0000000000fbfe87 in do_compile () at ../../gcc/toplev.c:2279
#21 0x0000000000fc0164 in toplev::main (this=0x7fffffffe56e, argc=2220, argv=0x2ecf450) at ../../gcc/t
   oplev.c:2414
#22 0x0000000001df1d19 in main (argc=53, argv=0x7fffffffe668) at ../../gcc/main.c:39

The issue is that ipa_agg_value_set_from_jfunc asks for parameter #3
which is out of range (function has 3 parmaeters). This is src_idx so I
think the jump function is not correctly updated after inlining. Which
also suggest the fact that the jump function causing problem is created
while producing inline clone:

Old value = 0
New value = 3
0x0000000000cc7115 in vec_copy_construct<ipa_agg_jf_item> (dst=0x7fff97e1cb08, src=0x7fff7e19e2c8, n=1) at ../.
   ./gcc/vec.h:523
523         ::new (static_cast<void*>(dst)) T (*src);
(gdb) bt
#0  0x0000000000cc7115 in vec_copy_construct<ipa_agg_jf_item> (dst=0x7fff97e1cb08, src=0x7fff7e19e2c8, n=1)
     at ../../gcc/vec.h:523
#1  0x0000000000cc506f in vec<ipa_agg_jf_item, va_gc, vl_embed>::copy (this=0x7fff7e19e2c0) at ../../gcc/vec.h:951
#2  0x0000000000cc2fe4 in vec_safe_copy<ipa_agg_jf_item, va_gc> (src=0x7fff7e19e2c0) at ../../gcc/vec.h:815
#3  0x0000000000cbb486 in ipa_edge_args_sum_t::duplicate (this=0x7fff8f20ac00, src=0x7fff9bcb4888, dst=0
    x7fff2ceac068, old_args=0x7fff7e193a50, new_args=0x7fff9a03ede0) at ../../gcc/ipa-prop.c:4217
#4  0x0000000000cc3f16 in call_summary<ipa_edge_args*>::symtab_duplication (edge1=0x7fff9bcb4888, edge2=0x7fff2ce
    ac068, data=0x7fff8f20ac00) at ../../gcc/symbol-summary.h:737
#5  0x00000000009a6186 in symbol_table::call_edge_duplication_hooks (this=0x7ffff70e1100, cs1=0x7fff9bcb4888,
cs2=0x7fff2ceac068) at ../../gcc/cgraph.c:455
#6  0x00000000009bef8a in cgraph_edge::clone (this=0x7fff9bcb4888, n=0x7fff9bc3c9a0, call_stmt=0x0,
    stmt_uid=5, num=..., den=..., update_original=true) at ../../gcc/cgraphcl
   ones.c:141
#7  0x00000000009bfd29 in cgraph_node::create_clone (this=0x7fff9bc3c840, new_decl=0x7fffc8b0b500,
    [36mprof_count=..., update_original=true, redirect_callers=..., call_duplication
    _hook=true, new_inlined_to=0x7fff9bf1b000, param_adjustments=0x0,
suffix=0x0) at ../../gcc/cgraphclones.c:393
#8  0x0000000001d213bc in clone_inlined_nodes (e=0x7fffa84b6f08, duplicate=true, update_orig
    inal=true, overall_size=0x2e617d0 <overall_size>) at ../../gcc/ipa-inline-transform.c:222
#9  0x0000000001d2200e in inline_call (e=0x7fffa84b6f08, update_original=true, new_edges=
    0x7fffffffe0c8, overall_size=0x2e617d0 <overall_size>, update_overall_summary=true, callee_
    removed=0x0) at ../../gcc/ipa-inline-transform.c:482
#10 0x0000000001d14af3 in inline_small_functions () at ../../gcc/ipa-inline.c:2141
#11 0x0000000001d165d8 in ipa_inline () at ../../gcc/ipa-inline.c:2613
#12 0x0000000001d17353 in (anonymous namespace)::pass_ipa_inline::execute (this=0x2f111e0) at ../../gcc/ipa-inline.c:3013
#13 0x0000000000e6c5a5 in execute_one_pass (pass=0x2f111e0) at ../../gcc/passes.c:2494
#14 0x0000000000e6d4ee in execute_ipa_pass_list (pass=0x2f111e0) at ../../gcc/passes.c:2921
#15 0x00000000008c6f19 in do_whole_program_analysis () at ../../gcc/lto/lto.c:469
#16 0x00000000008c721f in lto_main () at ../../gcc/lto/lto.c:641
#17 0x0000000000fbcc5b in compile_file () at ../../gcc/toplev.c:458

So it seems something is wrong with updating formal_id of the new jump
functions in update_jump_functions_after_inlining?

Honza
Feng Xue OS Nov. 15, 2019, 10:26 a.m. UTC | #13
I checked update_jump_functions_after_inlining(), and found one suspicious place:

  for (i = 0; i < count; i++)
    {
      struct ipa_jump_func *dst = ipa_get_ith_jump_func (args, i);
      if (!top)
        {
          ipa_set_jf_unknown (dst);
          <<<<<<<<<<<<<<<<<   we should also invalidate dst->agg.items.
          continue;
        }
      class ipa_polymorphic_call_context *dst_ctx
        = ipa_get_ith_polymorhic_call_context (args, i);   <<<< An irrelevant point:  and should we also do some kind of cleaning on dst_ctx?
Jan Hubicka Nov. 15, 2019, 2:05 p.m. UTC | #14
> I checked update_jump_functions_after_inlining(), and found one suspicious place:
> 
>   for (i = 0; i < count; i++)
>     {
>       struct ipa_jump_func *dst = ipa_get_ith_jump_func (args, i);
>       if (!top)
>         {
>           ipa_set_jf_unknown (dst);
>           <<<<<<<<<<<<<<<<<   we should also invalidate dst->agg.items.
This is a good catch. In meantime a smaller testcase surfaces in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92528
I am re-building Firefox with the patch I attache to the PR.

Honza
Tamar Christina Nov. 22, 2019, 12:39 p.m. UTC | #15
Hi Honza,
> 
> > I checked update_jump_functions_after_inlining(), and found one
> suspicious place:
> >
> >   for (i = 0; i < count; i++)
> >     {
> >       struct ipa_jump_func *dst = ipa_get_ith_jump_func (args, i);
> >       if (!top)
> >         {
> >           ipa_set_jf_unknown (dst);
> >           <<<<<<<<<<<<<<<<<   we should also invalidate dst->agg.items.
> This is a good catch. In meantime a smaller testcase surfaces in
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92528
> I am re-building Firefox with the patch I attache to the PR.
> 

Just curious how this went. Are the firefox issue sorted or is there something still to be done?

Thanks,
Tamar

> Honza
Jan Hubicka Nov. 22, 2019, 2:33 p.m. UTC | #16
> Hi Honza,
> > 
> > > I checked update_jump_functions_after_inlining(), and found one
> > suspicious place:
> > >
> > >   for (i = 0; i < count; i++)
> > >     {
> > >       struct ipa_jump_func *dst = ipa_get_ith_jump_func (args, i);
> > >       if (!top)
> > >         {
> > >           ipa_set_jf_unknown (dst);
> > >           <<<<<<<<<<<<<<<<<   we should also invalidate dst->agg.items.
> > This is a good catch. In meantime a smaller testcase surfaces in
> > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92528
> > I am re-building Firefox with the patch I attache to the PR.
> > 
> 
> Just curious how this went. Are the firefox issue sorted or is there something still to be done?

Yes, Firefox is working now for me and in fact I am just in the progress
of updating my branch at mozilla try servers to do some benchmarking
(still have few warnings to cache etc.).

There was additional interesting consequence of the patch in making
inliner noticeably slower both on cc1 compilation and Firefox:
propagating a lot more values made it to consider a lot more inlining
contextes.  So I finally pushed out patches treating non-linearities
there.

What remains to do is to fix the value ranges - ipa_set_jf_unknown
should not invalidate them.   I will try to do that soon.

Honza
> 
> Thanks,
> Tamar
> 
> > Honza
Tamar Christina Nov. 23, 2019, 2:36 p.m. UTC | #17
Hi Honza,

> > >
> > > > I checked update_jump_functions_after_inlining(), and found one
> > > suspicious place:
> > > >
> > > >   for (i = 0; i < count; i++)
> > > >     {
> > > >       struct ipa_jump_func *dst = ipa_get_ith_jump_func (args, i);
> > > >       if (!top)
> > > >         {
> > > >           ipa_set_jf_unknown (dst);
> > > >           <<<<<<<<<<<<<<<<<   we should also invalidate dst->agg.items.
> > > This is a good catch. In meantime a smaller testcase surfaces in
> > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92528
> > > I am re-building Firefox with the patch I attache to the PR.
> > >
> >
> > Just curious how this went. Are the firefox issue sorted or is there
> something still to be done?
> 
> Yes, Firefox is working now for me and in fact I am just in the progress of
> updating my branch at mozilla try servers to do some benchmarking (still
> have few warnings to cache etc.).
> 
> There was additional interesting consequence of the patch in making inliner
> noticeably slower both on cc1 compilation and Firefox:
> propagating a lot more values made it to consider a lot more inlining
> contextes.  So I finally pushed out patches treating non-linearities there.
> 
> What remains to do is to fix the value ranges - ipa_set_jf_unknown
> should not invalidate them.   I will try to do that soon.
> 

Ah, that's awesome, thanks for all the hard work!

Cheers,
Tamar

> Honza
> >
> > Thanks,
> > Tamar
> >
> > > Honza
Jan Hubicka Nov. 23, 2019, 2:40 p.m. UTC | #18
> Hi Honza,
> 
> > > >
> > > > > I checked update_jump_functions_after_inlining(), and found one
> > > > suspicious place:
> > > > >
> > > > >   for (i = 0; i < count; i++)
> > > > >     {
> > > > >       struct ipa_jump_func *dst = ipa_get_ith_jump_func (args, i);
> > > > >       if (!top)
> > > > >         {
> > > > >           ipa_set_jf_unknown (dst);
> > > > >           <<<<<<<<<<<<<<<<<   we should also invalidate dst->agg.items.
> > > > This is a good catch. In meantime a smaller testcase surfaces in
> > > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92528
> > > > I am re-building Firefox with the patch I attache to the PR.
> > > >
> > >
> > > Just curious how this went. Are the firefox issue sorted or is there
> > something still to be done?
> > 
> > Yes, Firefox is working now for me and in fact I am just in the progress of
> > updating my branch at mozilla try servers to do some benchmarking (still
> > have few warnings to cache etc.).
> > 
> > There was additional interesting consequence of the patch in making inliner
> > noticeably slower both on cc1 compilation and Firefox:
> > propagating a lot more values made it to consider a lot more inlining
> > contextes.  So I finally pushed out patches treating non-linearities there.
> > 
> > What remains to do is to fix the value ranges - ipa_set_jf_unknown
> > should not invalidate them.   I will try to do that soon.
> > 
> 
> Ah, that's awesome, thanks for all the hard work!

Just for record, the oroginal bug was fixed by Feng here :))

2019-11-15  Feng Xue  <fxue@os.amperecomputing.com>

        PR ipa/92528
        * ipa-prop.c (update_jump_functions_after_inlining):
	* Invalidate aggregate jump function when inlined-to caller
	has no edge summary.

Seems there is some work ahead since currently LTO built firefox crashes
at startup.  I am trying to diagnose that now.

Honza
> 
> Cheers,
> Tamar
> 
> > Honza
> > >
> > > Thanks,
> > > Tamar
> > >
> > > > Honza

Patch
diff mbox series

From 2dfa5e8b5a828ad8d46c2af5f66ee97fb04ebc16 Mon Sep 17 00:00:00 2001
From: Feng Xue <fxue@os.amperecomputing.com>
Date: Thu, 15 Aug 2019 15:47:14 +0800
Subject: [PATCH 1/2] temp

---
 gcc/ipa-cp.c                           | 498 ++++++++++++++++------
 gcc/ipa-fnsummary.c                    |  48 +--
 gcc/ipa-fnsummary.h                    |   8 +-
 gcc/ipa-inline-analysis.c              |   6 +-
 gcc/ipa-prop.c                         | 569 +++++++++++++++++++++----
 gcc/ipa-prop.h                         | 182 ++++++--
 gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c |   8 +-
 gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c |  77 ++++
 8 files changed, 1105 insertions(+), 291 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c

diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 8a5f8d362f6..e100c5f3426 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1287,23 +1287,23 @@  initialize_node_lattices (struct cgraph_node *node)
       }
 }
 
-/* Return the result of a (possibly arithmetic) pass through jump function
-   JFUNC on the constant value INPUT.  RES_TYPE is the type of the parameter
-   to which the result is passed.  Return NULL_TREE if that cannot be
-   determined or be considered an interprocedural invariant.  */
+/* Return the result of a (possibly arithmetic) operation on the constant
+   value INPUT.  OPERAND is 2nd operand for binary operation.  RES_TYPE is
+   the type of the parameter to which the result is passed.  Return
+   NULL_TREE if that cannot be determined or be considered an
+   interprocedural invariant.  */
 
 static tree
-ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input,
-				tree res_type)
+ipa_get_jf_arith_result (enum tree_code opcode, tree input, tree operand,
+			 tree res_type)
 {
   tree res;
 
-  if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
+  if (opcode == NOP_EXPR)
     return input;
   if (!is_gimple_ip_invariant (input))
     return NULL_TREE;
 
-  tree_code opcode = ipa_get_jf_pass_through_operation (jfunc);
   if (!res_type)
     {
       if (TREE_CODE_CLASS (opcode) == tcc_comparison)
@@ -1317,8 +1317,7 @@  ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input,
   if (TREE_CODE_CLASS (opcode) == tcc_unary)
     res = fold_unary (opcode, res_type, input);
   else
-    res = fold_binary (opcode, res_type, input,
-		       ipa_get_jf_pass_through_operand (jfunc));
+    res = fold_binary (opcode, res_type, input, operand);
 
   if (res && !is_gimple_ip_invariant (res))
     return NULL_TREE;
@@ -1326,6 +1325,21 @@  ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input,
   return res;
 }
 
+/* Return the result of a (possibly arithmetic) pass through jump function
+   JFUNC on the constant value INPUT.  RES_TYPE is the type of the parameter
+   to which the result is passed.  Return NULL_TREE if that cannot be
+   determined or be considered an interprocedural invariant.  */
+
+static tree
+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);
+}
+
 /* Return the result of an ancestor jump function JFUNC on the constant value
    INPUT.  Return NULL_TREE if that cannot be determined.  */
 
@@ -1459,6 +1473,146 @@  ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx,
   return ctx;
 }
 
+/* See if NODE is a clone with a known aggregate value at a given OFFSET of a
+   parameter with the given INDEX.  */
+
+static tree
+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;
+}
+
+/* Determine whether ITEM, jump function for an aggregate part, evaluates to a
+   single known constant value and if so, return it.  Otherwise return NULL.
+   NODE and INFO describes the caller node or the one it is inlined to, and
+   its related info.  */
+
+static tree
+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);
+}
+
+/* Determine whether AGG_JFUNC evaluates to a set of known constant value for
+   an aggregate and if so, return it.  Otherwise return an empty set.  NODE
+   and INFO describes the caller node or the one it is inlined to, and its
+   related info.  */
+
+struct ipa_agg_value_set
+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;
+}
+
 /* If checking is enabled, verify that no lattice is in the TOP state, i.e. not
    bottom, not containing a variable component and without any known value at
    the same time.  */
@@ -1635,16 +1789,25 @@  ipcp_lattice<valtype>::add_value (valtype newval, cgraph_edge *cs,
   return true;
 }
 
-/* Propagate values through a pass-through jump function JFUNC associated with
-   edge CS, taking values from SRC_LAT and putting them into DEST_LAT.  SRC_IDX
-   is the index of the source parameter.  PARM_TYPE is the type of the
-   parameter to which the result is passed.  */
+/* Propagate values through an arithmetic transformation described by a jump
+   function associated with edge CS, taking values from SRC_LAT and putting
+   them into DEST_LAT.  OPND1_TYPE is expected type for the values in SRC_LAT.
+   OPND2 is a constant value if transformation is a binary operation.
+   SRC_OFFSET specifies offset in an aggregate if SRC_LAT describes lattice of
+   a part of the aggregate.  SRC_IDX is the index of the source parameter.
+   RES_TYPE is the value type of result being propagated into.  Return true if
+   DEST_LAT changed.  */
 
 static bool
-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)
+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;
@@ -1654,17 +1817,22 @@  propagate_vals_across_pass_through (cgraph_edge *cs, ipa_jump_func *jfunc,
      number of them and we would just make lattices bottom.  If this condition
      is ever relaxed we have to detect self-feeding recursive calls in
      cgraph_edge_brings_value_p in a smarter way.  */
-  if ((ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR)
-      && ipa_edge_within_scc (cs))
+  if (opcode != NOP_EXPR && ipa_edge_within_scc (cs))
     ret = dest_lat->set_contains_variable ();
   else
     for (src_val = src_lat->values; src_val; src_val = src_val->next)
       {
-	tree cstval = ipa_get_jf_pass_through_result (jfunc, src_val->value,
-						      parm_type);
+	tree opnd1 = src_val->value;
+	tree cstval = NULL_TREE;
+
+	/* Skip source values that is incompatible with specified type.  */
+	if (!opnd1_type
+	    || useless_type_conversion_p (opnd1_type, TREE_TYPE (opnd1)))
+	  cstval = ipa_get_jf_arith_result (opcode, opnd1, opnd2, res_type);
 
 	if (cstval)
-	  ret |= dest_lat->add_value (cstval, cs, src_val, src_idx);
+	  ret |= dest_lat->add_value (cstval, cs, src_val, src_idx,
+				      src_offset);
 	else
 	  ret |= dest_lat->set_contains_variable ();
       }
@@ -1672,6 +1840,24 @@  propagate_vals_across_pass_through (cgraph_edge *cs, ipa_jump_func *jfunc,
   return ret;
 }
 
+/* Propagate values through a pass-through jump function JFUNC associated with
+   edge CS, taking values from SRC_LAT and putting them into DEST_LAT.  SRC_IDX
+   is the index of the source parameter.  PARM_TYPE is the type of the
+   parameter to which the result is passed.  */
+
+static bool
+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);
+}
+
 /* Propagate values through an ancestor jump function JFUNC associated with
    edge CS, taking values from SRC_LAT and putting them into DEST_LAT.  SRC_IDX
    is the index of the source parameter.  */
@@ -1832,7 +2018,6 @@  propagate_context_across_jump_function (cgraph_edge *cs,
 	      added_sth = true;
 	    }
 	}
-
     }
 
  prop_fail:
@@ -2187,6 +2372,85 @@  agg_pass_through_permissible_p (class ipcp_param_lattices *src_plats,
 	|| ipa_get_jf_pass_through_agg_preserved (jfunc));
 }
 
+/* Propagate values through ITEM, jump function for a part of an aggregate,
+   into corresponding aggregate lattice AGLAT.  CS is the call graph edge
+   associated with the jump function.  Return true if AGLAT changed in any
+   way.  */
+
+static bool
+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;
+}
+
 /* Propagate scalar values across jump function JFUNC that is associated with
    edge CS and put the values into DEST_LAT.  */
 
@@ -2254,15 +2518,14 @@  propagate_aggs_across_jump_function (struct cgraph_edge *cs,
 	{
 	  HOST_WIDE_INT val_size;
 
-	  if (item->offset < 0)
+	  if (item->offset < 0 || item->jftype == IPA_JF_UNKNOWN)
 	    continue;
-	  gcc_checking_assert (is_gimple_ip_invariant (item->value));
-	  val_size = tree_to_uhwi (TYPE_SIZE (TREE_TYPE (item->value)));
+	  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 |= (*aglat)->add_value (item->value, cs, NULL, 0, 0);
+	      ret |= propagate_aggregate_lattice (cs, item, *aglat);
 	      aglat = &(*aglat)->next;
 	    }
 	  else if (dest_plats->aggs_bottom)
@@ -2375,7 +2638,7 @@  static tree
 ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
 				vec<tree> known_csts,
 				vec<ipa_polymorphic_call_context> known_contexts,
-				vec<ipa_agg_jump_function_p> known_aggs,
+				vec<ipa_agg_value_set> known_aggs,
 				struct ipa_agg_replacement_value *agg_reps,
 				bool *speculative)
 {
@@ -2413,9 +2676,9 @@  ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
 	    }
 	  if (!t)
 	    {
-	      struct ipa_agg_jump_function *agg;
+	      struct ipa_agg_value_set *agg;
 	      if (known_aggs.length () > (unsigned int) param_index)
-		agg = known_aggs[param_index];
+		agg = &known_aggs[param_index];
 	      else
 		agg = NULL;
 	      bool from_global_constant;
@@ -2469,8 +2732,7 @@  ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
   if (!t && known_aggs.length () > (unsigned int) param_index
       && !ie->indirect_info->by_ref)
     {
-      struct ipa_agg_jump_function *agg;
-      agg = known_aggs[param_index];
+      struct ipa_agg_value_set *agg = &known_aggs[param_index];
       t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
 				      ie->indirect_info->offset, true);
     }
@@ -2592,7 +2854,7 @@  tree
 ipa_get_indirect_edge_target (struct cgraph_edge *ie,
 			      vec<tree> known_csts,
 			      vec<ipa_polymorphic_call_context> known_contexts,
-			      vec<ipa_agg_jump_function_p> known_aggs,
+			      vec<ipa_agg_value_set> known_aggs,
 			      bool *speculative)
 {
   return ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts,
@@ -2606,7 +2868,7 @@  static int
 devirtualization_time_bonus (struct cgraph_node *node,
 			     vec<tree> known_csts,
 			     vec<ipa_polymorphic_call_context> known_contexts,
-			     vec<ipa_agg_jump_function_p> known_aggs)
+			     vec<ipa_agg_value_set> known_aggs)
 {
   struct cgraph_edge *ie;
   int res = 0;
@@ -2741,25 +3003,25 @@  good_cloning_opportunity_p (struct cgraph_node *node, int time_benefit,
 /* Return all context independent values from aggregate lattices in PLATS in a
    vector.  Return NULL if there are none.  */
 
-static vec<ipa_agg_jf_item, va_gc> *
+static vec<ipa_agg_value>
 context_independent_aggregate_values (class ipcp_param_lattices *plats)
 {
-  vec<ipa_agg_jf_item, va_gc> *res = NULL;
+  vec<ipa_agg_value> res = vNULL;
 
   if (plats->aggs_bottom
       || plats->aggs_contain_variable
       || plats->aggs_count == 0)
-    return NULL;
+    return vNULL;
 
   for (struct ipcp_agg_lattice *aglat = plats->aggs;
        aglat;
        aglat = aglat->next)
     if (aglat->is_single_const ())
       {
-	struct ipa_agg_jf_item item;
+	struct ipa_agg_value item;
 	item.offset = aglat->offset;
 	item.value = aglat->values->value;
-	vec_safe_push (res, item);
+	res.safe_push (item);
       }
   return res;
 }
@@ -2775,7 +3037,7 @@  gather_context_independent_values (class ipa_node_params *info,
 				   vec<tree> *known_csts,
 				   vec<ipa_polymorphic_call_context>
 				   *known_contexts,
-				   vec<ipa_agg_jump_function> *known_aggs,
+				   vec<ipa_agg_value_set> *known_aggs,
 				   int *removable_params_cost)
 {
   int i, count = ipa_get_param_count (info);
@@ -2825,40 +3087,20 @@  gather_context_independent_values (class ipa_node_params *info,
 
       if (known_aggs)
 	{
-	  vec<ipa_agg_jf_item, va_gc> *agg_items;
-	  struct ipa_agg_jump_function *ajf;
+	  vec<ipa_agg_value> agg_items;
+	  struct ipa_agg_value_set *agg;
 
 	  agg_items = context_independent_aggregate_values (plats);
-	  ajf = &(*known_aggs)[i];
-	  ajf->items = agg_items;
-	  ajf->by_ref = plats->aggs_by_ref;
-	  ret |= agg_items != NULL;
+	  agg = &(*known_aggs)[i];
+	  agg->items = agg_items;
+	  agg->by_ref = plats->aggs_by_ref;
+	  ret |= !agg_items.is_empty ();
 	}
     }
 
   return ret;
 }
 
-/* The current interface in ipa-inline-analysis requires a pointer vector.
-   Create it.
-
-   FIXME: That interface should be re-worked, this is slightly silly.  Still,
-   I'd like to discuss how to change it first and this demonstrates the
-   issue.  */
-
-static vec<ipa_agg_jump_function_p>
-agg_jmp_p_vec_for_t_vec (vec<ipa_agg_jump_function> known_aggs)
-{
-  vec<ipa_agg_jump_function_p> ret;
-  struct ipa_agg_jump_function *ajf;
-  int i;
-
-  ret.create (known_aggs.length ());
-  FOR_EACH_VEC_ELT (known_aggs, i, ajf)
-    ret.quick_push (ajf);
-  return ret;
-}
-
 /* Perform time and size measurement of NODE with the context given in
    KNOWN_CSTS, KNOWN_CONTEXTS and KNOWN_AGGS, calculate the benefit and cost
    given BASE_TIME of the node without specialization, REMOVABLE_PARAMS_COST of
@@ -2868,7 +3110,7 @@  agg_jmp_p_vec_for_t_vec (vec<ipa_agg_jump_function> known_aggs)
 static void
 perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
 			       vec<ipa_polymorphic_call_context> known_contexts,
-			       vec<ipa_agg_jump_function_p> known_aggs_ptrs,
+			       vec<ipa_agg_value_set> known_aggs,
 			       int removable_params_cost,
 			       int est_move_cost, ipcp_value_base *val)
 {
@@ -2877,7 +3119,7 @@  perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
   ipa_hints hints;
 
   estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
-				     known_aggs_ptrs, &size, &time,
+				     known_aggs, &size, &time,
 				     &base_time, &hints);
   base_time -= time;
   if (base_time > 65535)
@@ -2891,7 +3133,7 @@  perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
   else
     time_benefit = base_time.to_int ()
       + devirtualization_time_bonus (node, known_csts, known_contexts,
-				     known_aggs_ptrs)
+				     known_aggs)
       + hint_time_bonus (hints)
       + removable_params_cost + est_move_cost;
 
@@ -2917,8 +3159,7 @@  estimate_local_effects (struct cgraph_node *node)
   int i, count = ipa_get_param_count (info);
   vec<tree> known_csts;
   vec<ipa_polymorphic_call_context> known_contexts;
-  vec<ipa_agg_jump_function> known_aggs;
-  vec<ipa_agg_jump_function_p> known_aggs_ptrs;
+  vec<ipa_agg_value_set> known_aggs;
   bool always_const;
   int removable_params_cost;
 
@@ -2931,9 +3172,8 @@  estimate_local_effects (struct cgraph_node *node)
   always_const = gather_context_independent_values (info, &known_csts,
 						    &known_contexts, &known_aggs,
 						    &removable_params_cost);
-  known_aggs_ptrs = agg_jmp_p_vec_for_t_vec (known_aggs);
   int devirt_bonus = devirtualization_time_bonus (node, known_csts,
-					   known_contexts, known_aggs_ptrs);
+					   known_contexts, known_aggs);
   if (always_const || devirt_bonus
       || (removable_params_cost && node->can_change_signature))
     {
@@ -2946,7 +3186,7 @@  estimate_local_effects (struct cgraph_node *node)
       node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
 					      false);
       estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
-					 known_aggs_ptrs, &size, &time,
+					 known_aggs, &size, &time,
 					 &base_time, &hints);
       time -= devirt_bonus;
       time -= hint_time_bonus (hints);
@@ -3009,7 +3249,7 @@  estimate_local_effects (struct cgraph_node *node)
 
 	  int emc = estimate_move_cost (TREE_TYPE (val->value), true);
 	  perform_estimation_of_a_value (node, known_csts, known_contexts,
-					 known_aggs_ptrs,
+					 known_aggs,
 					 removable_params_cost, emc, val);
 
 	  if (dump_file && (dump_flags & TDF_DETAILS))
@@ -3044,7 +3284,7 @@  estimate_local_effects (struct cgraph_node *node)
 	{
 	  known_contexts[i] = val->value;
 	  perform_estimation_of_a_value (node, known_csts, known_contexts,
-					 known_aggs_ptrs,
+					 known_aggs,
 					 removable_params_cost, 0, val);
 
 	  if (dump_file && (dump_flags & TDF_DETAILS))
@@ -3063,13 +3303,13 @@  estimate_local_effects (struct cgraph_node *node)
   for (i = 0; i < count; i++)
     {
       class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
-      struct ipa_agg_jump_function *ajf;
+      struct ipa_agg_value_set *agg;
       struct ipcp_agg_lattice *aglat;
 
       if (plats->aggs_bottom || !plats->aggs)
 	continue;
 
-      ajf = &known_aggs[i];
+      agg = &known_aggs[i];
       for (aglat = plats->aggs; aglat; aglat = aglat->next)
 	{
 	  ipcp_value<tree> *val;
@@ -3081,14 +3321,14 @@  estimate_local_effects (struct cgraph_node *node)
 
 	  for (val = aglat->values; val; val = val->next)
 	    {
-	      struct ipa_agg_jf_item item;
+	      struct ipa_agg_value item;
 
 	      item.offset = aglat->offset;
 	      item.value = val->value;
-	      vec_safe_push (ajf->items, item);
+	      agg->items.safe_push (item);
 
 	      perform_estimation_of_a_value (node, known_csts, known_contexts,
-					     known_aggs_ptrs,
+					     known_aggs,
 					     removable_params_cost, 0, val);
 
 	      if (dump_file && (dump_flags & TDF_DETAILS))
@@ -3104,18 +3344,14 @@  estimate_local_effects (struct cgraph_node *node)
 			   val->local_time_benefit, val->local_size_cost);
 		}
 
-	      ajf->items->pop ();
+	      agg->items.pop ();
 	    }
 	}
     }
 
-  for (i = 0; i < count; i++)
-    vec_free (known_aggs[i].items);
-
   known_csts.release ();
   known_contexts.release ();
-  known_aggs.release ();
-  known_aggs_ptrs.release ();
+  ipa_release_agg_values (known_aggs);
 }
 
 
@@ -3483,26 +3719,6 @@  edge_clone_summary_t::duplicate (cgraph_edge *src_edge, cgraph_edge *dst_edge,
   src_data->next_clone = dst_edge;
 }
 
-/* See if NODE is a clone with a known aggregate value at a given OFFSET of a
-   parameter with the given INDEX.  */
-
-static tree
-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;
-}
-
 /* Return true is NODE is DEST or its clone for all contexts.  */
 
 static bool
@@ -4185,10 +4401,10 @@  find_more_contexts_for_caller_subset (cgraph_node *node,
 /* Go through PLATS and create a vector of values consisting of values and
    offsets (minus OFFSET) of lattices that contain only a single value.  */
 
-static vec<ipa_agg_jf_item>
+static vec<ipa_agg_value>
 copy_plats_to_inter (class ipcp_param_lattices *plats, HOST_WIDE_INT offset)
 {
-  vec<ipa_agg_jf_item> res = vNULL;
+  vec<ipa_agg_value> res = vNULL;
 
   if (!plats->aggs || plats->aggs_contain_variable || plats->aggs_bottom)
     return vNULL;
@@ -4196,7 +4412,7 @@  copy_plats_to_inter (class ipcp_param_lattices *plats, HOST_WIDE_INT offset)
   for (struct ipcp_agg_lattice *aglat = plats->aggs; aglat; aglat = aglat->next)
     if (aglat->is_single_const ())
       {
-	struct ipa_agg_jf_item ti;
+	struct ipa_agg_value ti;
 	ti.offset = aglat->offset - offset;
 	ti.value = aglat->values->value;
 	res.safe_push (ti);
@@ -4209,11 +4425,11 @@  copy_plats_to_inter (class ipcp_param_lattices *plats, HOST_WIDE_INT offset)
 
 static void
 intersect_with_plats (class ipcp_param_lattices *plats,
-		      vec<ipa_agg_jf_item> *inter,
+		      vec<ipa_agg_value> *inter,
 		      HOST_WIDE_INT offset)
 {
   struct ipcp_agg_lattice *aglat;
-  struct ipa_agg_jf_item *item;
+  struct ipa_agg_value *item;
   int k;
 
   if (!plats->aggs || plats->aggs_contain_variable || plats->aggs_bottom)
@@ -4251,18 +4467,18 @@  intersect_with_plats (class ipcp_param_lattices *plats,
 /* Copy aggregate replacement values of NODE (which is an IPA-CP clone) to the
    vector result while subtracting OFFSET from the individual value offsets.  */
 
-static vec<ipa_agg_jf_item>
+static vec<ipa_agg_value>
 agg_replacements_to_vector (struct cgraph_node *node, int index,
 			    HOST_WIDE_INT offset)
 {
   struct ipa_agg_replacement_value *av;
-  vec<ipa_agg_jf_item> res = vNULL;
+  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_jf_item item;
+      struct ipa_agg_value item;
       gcc_checking_assert (av->value);
       item.offset = av->offset - offset;
       item.value = av->value;
@@ -4278,11 +4494,11 @@  agg_replacements_to_vector (struct cgraph_node *node, int index,
 
 static void
 intersect_with_agg_replacements (struct cgraph_node *node, int index,
-				 vec<ipa_agg_jf_item> *inter,
+				 vec<ipa_agg_value> *inter,
 				 HOST_WIDE_INT offset)
 {
   struct ipa_agg_replacement_value *srcvals;
-  struct ipa_agg_jf_item *item;
+  struct ipa_agg_value *item;
   int i;
 
   srcvals = ipa_get_agg_replacements_for_node (node);
@@ -4319,9 +4535,9 @@  intersect_with_agg_replacements (struct cgraph_node *node, int index,
    copy all incoming values to it.  If we determine we ended up with no values
    whatsoever, return a released vector.  */
 
-static vec<ipa_agg_jf_item>
+static vec<ipa_agg_value>
 intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
-				vec<ipa_agg_jf_item> inter)
+				vec<ipa_agg_value> inter)
 {
   struct ipa_jump_func *jfunc;
   jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), index);
@@ -4402,12 +4618,26 @@  intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
     }
   else if (jfunc->agg.items)
     {
-      struct ipa_agg_jf_item *item;
+      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++)
-	  inter.safe_push ((*jfunc->agg.items)[i]);
+	  {
+	    struct ipa_agg_jf_item *agg_item = &(*jfunc->agg.items)[i];
+	    tree value = ipa_agg_value_from_node (caller_info, cs->caller,
+						  agg_item);
+	    if (value)
+	      {
+		struct ipa_agg_value agg_value;
+
+		agg_value.offset = agg_item->offset;
+		agg_value.value = value;
+
+		inter.safe_push (agg_value);
+	      }
+	  }
       else
 	FOR_EACH_VEC_ELT (inter, k, item)
 	  {
@@ -4425,9 +4655,10 @@  intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
 		  break;
 		if (ti->offset == item->offset)
 		  {
-		    gcc_checking_assert (ti->value);
-		    if (values_equal_for_ipcp_p (item->value,
-						 ti->value))
+		    tree value = ipa_agg_value_from_node (caller_info,
+							  cs->caller, ti);
+		    if (value
+			&& values_equal_for_ipcp_p (item->value, value))
 		      found = true;
 		    break;
 		  }
@@ -4440,7 +4671,7 @@  intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
   else
     {
       inter.release ();
-      return vec<ipa_agg_jf_item>();
+      return vNULL;
     }
   return inter;
 }
@@ -4473,8 +4704,8 @@  find_aggregate_values_for_callers_subset (struct cgraph_node *node,
   for (i = 0; i < count; i++)
     {
       struct cgraph_edge *cs;
-      vec<ipa_agg_jf_item> inter = vNULL;
-      struct ipa_agg_jf_item *item;
+      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;
 
@@ -4581,7 +4812,7 @@  cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs,
 
   for (i = 0; i < count; i++)
     {
-      static vec<ipa_agg_jf_item> values = vec<ipa_agg_jf_item>();
+      static vec<ipa_agg_value> values = vNULL;
       class ipcp_param_lattices *plats;
       bool interesting = false;
       for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next)
@@ -4604,7 +4835,7 @@  cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs,
       for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next)
 	if (aggval->index == i)
 	  {
-	    struct ipa_agg_jf_item *item;
+	    struct ipa_agg_value *item;
 	    int j;
 	    bool found = false;
 	    FOR_EACH_VEC_ELT (values, j, item)
@@ -4842,7 +5073,6 @@  decide_whether_version_node (struct cgraph_node *node)
   int i, count = ipa_get_param_count (info);
   vec<tree> known_csts;
   vec<ipa_polymorphic_call_context> known_contexts;
-  vec<ipa_agg_jump_function> known_aggs = vNULL;
   bool ret = false;
 
   if (count == 0)
@@ -4853,8 +5083,7 @@  decide_whether_version_node (struct cgraph_node *node)
 	     node->dump_name ());
 
   gather_context_independent_values (info, &known_csts, &known_contexts,
-				  info->do_clone_for_all_contexts ? &known_aggs
-				  : NULL, NULL);
+				     NULL, NULL);
 
   for (i = 0; i < count;i++)
     {
@@ -4923,9 +5152,6 @@  decide_whether_version_node (struct cgraph_node *node)
       info = IPA_NODE_REF (node);
       info->do_clone_for_all_contexts = false;
       IPA_NODE_REF (clone)->is_all_contexts_clone = true;
-      for (i = 0; i < count; i++)
-	vec_free (known_aggs[i].items);
-      known_aggs.release ();
       ret = true;
     }
   else
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 4a3cb760a26..d774cccd75f 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -307,9 +307,9 @@  set_hint_predicate (predicate **p, predicate new_predicate)
    the fact that parameter is indeed a constant.
 
    KNOWN_VALS is partial mapping of parameters of NODE to constant values.
-   KNOWN_AGGS is a vector of aggreggate jump functions for each parameter.
-   Return clause of possible truths. When INLINE_P is true, assume that we are
-   inlining.
+   KNOWN_AGGS is a vector of aggreggate known offset/value set for each
+   parameter.  Return clause of possible truths.  When INLINE_P is true, assume
+   that we are inlining.
 
    ERROR_MARK means compile time invariant.  */
 
@@ -317,8 +317,7 @@  static void
 evaluate_conditions_for_known_args (struct cgraph_node *node,
 				    bool inline_p,
 				    vec<tree> known_vals,
-				    vec<ipa_agg_jump_function_p>
-				    known_aggs,
+				    vec<ipa_agg_value_set> known_aggs,
 				    clause_t *ret_clause,
 				    clause_t *ret_nonspec_clause)
 {
@@ -350,7 +349,7 @@  evaluate_conditions_for_known_args (struct cgraph_node *node,
 
       if (c->agg_contents)
 	{
-	  struct ipa_agg_jump_function *agg;
+	  struct ipa_agg_value_set *agg;
 
 	  if (c->code == predicate::changed
 	      && !c->by_ref
@@ -359,7 +358,7 @@  evaluate_conditions_for_known_args (struct cgraph_node *node,
 
 	  if (known_aggs.exists ())
 	    {
-	      agg = known_aggs[c->operand_num];
+	      agg = &known_aggs[c->operand_num];
 	      val = ipa_find_agg_cst_for_param (agg, known_vals[c->operand_num],
 						c->offset, c->by_ref);
 	    }
@@ -446,12 +445,12 @@  evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
 			      vec<tree> *known_vals_ptr,
 			      vec<ipa_polymorphic_call_context>
 			      *known_contexts_ptr,
-			      vec<ipa_agg_jump_function_p> *known_aggs_ptr)
+			      vec<ipa_agg_value_set> *known_aggs_ptr)
 {
   struct cgraph_node *callee = e->callee->ultimate_alias_target ();
   class ipa_fn_summary *info = ipa_fn_summaries->get (callee);
   vec<tree> known_vals = vNULL;
-  vec<ipa_agg_jump_function_p> known_aggs = vNULL;
+  vec<ipa_agg_value_set> known_aggs = vNULL;
   class ipa_edge_args *args;
 
   if (clause_ptr)
@@ -466,14 +465,16 @@  evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
       && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr)
       && (args = IPA_EDGE_REF (e)) != NULL)
     {
+      struct cgraph_node *caller;
       class ipa_node_params *caller_parms_info, *callee_pi;
       class ipa_call_summary *es = ipa_call_summaries->get (e);
       int i, count = ipa_get_cs_argument_count (args);
 
       if (e->caller->inlined_to)
-	caller_parms_info = IPA_NODE_REF (e->caller->inlined_to);
+	caller = e->caller->inlined_to;
       else
-	caller_parms_info = IPA_NODE_REF (e->caller);
+	caller = e->caller;
+      caller_parms_info = IPA_NODE_REF (caller);
       callee_pi = IPA_NODE_REF (e->callee);
 
       if (count && (info->conds || known_vals_ptr))
@@ -508,10 +509,9 @@  evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
 	  if (known_contexts_ptr)
 	    (*known_contexts_ptr)[i]
 	      = ipa_context_from_jfunc (caller_parms_info, e, i, jf);
-	  /* TODO: When IPA-CP starts propagating and merging aggregate jump
-	     functions, use its knowledge of the caller too, just like the
-	     scalar case above.  */
-	  known_aggs[i] = &jf->agg;
+
+	  known_aggs[i] = ipa_agg_value_set_from_jfunc (caller_parms_info,
+							caller, &jf->agg);
 	}
     }
   else if (e->call_stmt && !e->call_stmt_cannot_inline_p
@@ -543,7 +543,7 @@  evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
   if (known_aggs_ptr)
     *known_aggs_ptr = known_aggs;
   else
-    known_aggs.release ();
+    ipa_release_agg_values (known_aggs);
 }
 
 
@@ -2835,7 +2835,7 @@  estimate_edge_devirt_benefit (struct cgraph_edge *ie,
 			      int *size, int *time,
 			      vec<tree> known_vals,
 			      vec<ipa_polymorphic_call_context> known_contexts,
-			      vec<ipa_agg_jump_function_p> known_aggs)
+			      vec<ipa_agg_value_set> known_aggs)
 {
   tree target;
   struct cgraph_node *callee;
@@ -2884,7 +2884,7 @@  estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *min_size,
 			     int prob,
 			     vec<tree> known_vals,
 			     vec<ipa_polymorphic_call_context> known_contexts,
-			     vec<ipa_agg_jump_function_p> known_aggs,
+			     vec<ipa_agg_value_set> known_aggs,
 			     ipa_hints *hints)
 {
   class ipa_call_summary *es = ipa_call_summaries->get (e);
@@ -2919,7 +2919,7 @@  estimate_calls_size_and_time (struct cgraph_node *node, int *size,
 			      clause_t possible_truths,
 			      vec<tree> known_vals,
 			      vec<ipa_polymorphic_call_context> known_contexts,
-			      vec<ipa_agg_jump_function_p> known_aggs)
+			      vec<ipa_agg_value_set> known_aggs)
 {
   struct cgraph_edge *e;
   for (e = node->callees; e; e = e->next_callee)
@@ -2978,7 +2978,7 @@  ipa_call_context::ipa_call_context (cgraph_node *node,
 				    vec<tree> known_vals,
 				    vec<ipa_polymorphic_call_context>
 				   	 known_contexts,
-				    vec<ipa_agg_jump_function_p> known_aggs,
+				    vec<ipa_agg_value_set> known_aggs,
 				    vec<inline_param_summary>
 				   	 inline_param_summary)
 : m_node (node), m_possible_truths (possible_truths),
@@ -3053,7 +3053,7 @@  ipa_call_context::duplicate_from (const ipa_call_context &ctx)
 	if (ipa_is_param_used_by_indirect_call (params_summary, i)
 	    && ctx.m_known_aggs[i])
 	  {
-	    m_known_aggs = ctx.m_known_aggs.copy ();
+	    m_known_aggs = ipa_copy_agg_values (ctx.m_known_aggs);
 	    break;
 	  }
     }
@@ -3072,7 +3072,7 @@  ipa_call_context::release (bool all)
     return;
   m_known_vals.release ();
   m_known_contexts.release ();
-  m_known_aggs.release ();
+  ipa_release_agg_values (m_known_aggs);
   if (all)
     m_inline_param_summary.release ();
 }
@@ -3184,7 +3184,7 @@  ipa_call_context::equal_to (const ipa_call_context &ctx)
 		return false;
 	      continue;
 	    }
-	  if (m_known_aggs[i] != ctx.m_known_aggs[i])
+	  if (!m_known_aggs[i].equal_to (ctx.m_known_aggs[i]))
 	    return false;
 	}
     }
@@ -3332,7 +3332,7 @@  estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
 				   vec<tree> known_vals,
 				   vec<ipa_polymorphic_call_context>
 				   known_contexts,
-				   vec<ipa_agg_jump_function_p> known_aggs,
+				   vec<ipa_agg_value_set> known_aggs,
 				   int *ret_size, sreal *ret_time,
 				   sreal *ret_nonspec_time,
 				   ipa_hints *hints)
diff --git a/gcc/ipa-fnsummary.h b/gcc/ipa-fnsummary.h
index 91488a99881..50e73a569a8 100644
--- a/gcc/ipa-fnsummary.h
+++ b/gcc/ipa-fnsummary.h
@@ -293,7 +293,7 @@  public:
 		    clause_t nonspec_possible_truths,
 		    vec<tree> known_vals,
 		    vec<ipa_polymorphic_call_context> known_contexts,
-		    vec<ipa_agg_jump_function_p> known_aggs,
+		    vec<ipa_agg_value_set> known_aggs,
 		    vec<inline_param_summary> m_inline_param_summary);
   ipa_call_context ()
   : m_node(NULL)
@@ -329,7 +329,7 @@  private:
   /* Vector describing known polymorphic call contexts.  */
   vec<ipa_polymorphic_call_context> m_known_contexts;
   /* Vector describing known aggregate values.  */
-  vec<ipa_agg_jump_function_p> m_known_aggs;
+  vec<ipa_agg_value_set> m_known_aggs;
 };
 
 extern fast_call_summary <ipa_call_summary *, va_heap> *ipa_call_summaries;
@@ -345,7 +345,7 @@  void inline_analyze_function (struct cgraph_node *node);
 void estimate_ipcp_clone_size_and_time (struct cgraph_node *,
 					vec<tree>,
 					vec<ipa_polymorphic_call_context>,
-					vec<ipa_agg_jump_function_p>,
+					vec<ipa_agg_value_set>,
 					int *, sreal *, sreal *,
 				        ipa_hints *);
 void ipa_merge_fn_summary_after_inlining (struct cgraph_edge *edge);
@@ -360,7 +360,7 @@  void evaluate_properties_for_edge (struct cgraph_edge *e,
 				   vec<tree> *known_vals_ptr,
 				   vec<ipa_polymorphic_call_context>
 				   *known_contexts_ptr,
-				   vec<ipa_agg_jump_function_p> *);
+				   vec<ipa_agg_value_set> *);
 
 void ipa_fnsummary_c_finalize (void);
 HOST_WIDE_INT ipa_get_stack_frame_offset (struct cgraph_node *node);
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index 436310596ac..0d4a85b9e1e 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -189,7 +189,7 @@  do_estimate_edge_time (struct cgraph_edge *edge)
   clause_t clause, nonspec_clause;
   vec<tree> known_vals;
   vec<ipa_polymorphic_call_context> known_contexts;
-  vec<ipa_agg_jump_function_p> known_aggs;
+  vec<ipa_agg_value_set> known_aggs;
   class ipa_call_summary *es = ipa_call_summaries->get (edge);
   int min_size = -1;
 
@@ -286,7 +286,7 @@  do_estimate_edge_size (struct cgraph_edge *edge)
   clause_t clause, nonspec_clause;
   vec<tree> known_vals;
   vec<ipa_polymorphic_call_context> known_contexts;
-  vec<ipa_agg_jump_function_p> known_aggs;
+  vec<ipa_agg_value_set> known_aggs;
 
   /* When we do caching, use do_estimate_edge_time to populate the entry.  */
 
@@ -325,7 +325,7 @@  do_estimate_edge_hints (struct cgraph_edge *edge)
   clause_t clause, nonspec_clause;
   vec<tree> known_vals;
   vec<ipa_polymorphic_call_context> known_contexts;
-  vec<ipa_agg_jump_function_p> known_aggs;
+  vec<ipa_agg_value_set> known_aggs;
 
   /* When we do caching, use do_estimate_edge_time to populate the entry.  */
 
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 83cf4d1c7ba..194c31ff54a 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -361,18 +361,45 @@  ipa_print_node_jump_functions_for_edge (FILE *f, struct cgraph_edge *cs)
 
 	  fprintf (f, "         Aggregate passed by %s:\n",
 		   jump_func->agg.by_ref ? "reference" : "value");
-	  FOR_EACH_VEC_SAFE_ELT (jump_func->agg.items, j, item)
+	  FOR_EACH_VEC_ELT (*jump_func->agg.items, j, item)
 	    {
 	      fprintf (f, "           offset: " HOST_WIDE_INT_PRINT_DEC ", ",
 		       item->offset);
-	      if (TYPE_P (item->value))
-		fprintf (f, "clobber of " HOST_WIDE_INT_PRINT_DEC " bits",
-			 tree_to_uhwi (TYPE_SIZE (item->value)));
-	      else
+	      fprintf (f, "type: ");
+	      print_generic_expr (f, item->type);
+	      fprintf (f, ", ");
+	      if (item->jftype == IPA_JF_PASS_THROUGH)
+		fprintf (f, "PASS THROUGH: %d,",
+			 item->value.pass_through.formal_id);
+	      else if (item->jftype == IPA_JF_LOAD_AGG)
+		{
+		  fprintf (f, "LOAD AGG: %d",
+			   item->value.pass_through.formal_id);
+		  fprintf (f, " [offset: " HOST_WIDE_INT_PRINT_DEC ", by %s],",
+			   item->value.load_agg.offset,
+			   item->value.load_agg.by_ref ? "reference"
+						       : "value");
+		}
+
+	      if (item->jftype == IPA_JF_PASS_THROUGH
+		  || item->jftype == IPA_JF_LOAD_AGG)
+		{
+		  fprintf (f, " op %s",
+		     get_tree_code_name (item->value.pass_through.operation));
+		  if (item->value.pass_through.operation != NOP_EXPR)
+		    {
+		      fprintf (f, " ");
+		      print_generic_expr (f, item->value.pass_through.operand);
+		    }
+		}
+	      else if (item->jftype == IPA_JF_CONST)
 		{
-		  fprintf (f, "cst: ");
-		  print_generic_expr (f, item->value);
+		  fprintf (f, "CONST: ");
+		  print_generic_expr (f, item->value.constant);
 		}
+	      else if (item->jftype == IPA_JF_UNKNOWN)
+		fprintf (f, "UNKNOWN: " HOST_WIDE_INT_PRINT_DEC " bits",
+			 tree_to_uhwi (TYPE_SIZE (item->type)));
 	      fprintf (f, "\n");
 	    }
 	}
@@ -1137,6 +1164,67 @@  ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
   return false;
 }
 
+/* If STMT is an assignment that loads a value from a parameter declaration,
+   or from an aggregate passed as the parameter either by value or reference,
+   return the index of the parameter in ipa_node_params.  Otherwise return -1.
+
+   FBI holds gathered information about the function.  INFO describes
+   parameters of the function, STMT is the assignment statement.  If it is a
+   memory load from an aggregate, *OFFSET_P is filled with offset within the
+   aggregate, and *BY_REF_P specifies whether the aggregate is passed by
+   reference.  */
+
+static int
+load_from_unmodified_param_or_agg (struct ipa_func_body_info *fbi,
+				   class ipa_node_params *info,
+				   gimple *stmt,
+				   HOST_WIDE_INT *offset_p,
+				   bool *by_ref_p)
+{
+  int index = load_from_unmodified_param (fbi, info->descriptors, stmt);
+  poly_int64 size;
+
+  /* Load value from a parameter declaration.  */
+  if (index >= 0)
+    {
+      *offset_p = -1;
+      return index;
+    }
+
+  if (!gimple_assign_load_p (stmt))
+    return -1;
+
+  tree rhs = gimple_assign_rhs1 (stmt);
+
+  /* Skip memory reference containing VIEW_CONVERT_EXPR.  */
+  for (tree t = rhs; handled_component_p (t); t = TREE_OPERAND (t, 0))
+    if (TREE_CODE (t) == VIEW_CONVERT_EXPR)
+      return -1;
+
+  /* Skip memory reference containing bit-field.  */
+  if (TREE_CODE (rhs) == BIT_FIELD_REF
+      || contains_bitfld_component_ref_p (rhs))
+    return -1;
+
+  if (!ipa_load_from_parm_agg (fbi, info->descriptors, stmt, rhs, &index,
+			       offset_p, &size, by_ref_p))
+    return -1;
+
+  gcc_assert (!maybe_ne (tree_to_poly_int64 (TYPE_SIZE (TREE_TYPE (rhs))),
+			 size));
+  if (!*by_ref_p)
+    {
+      tree param_type = ipa_get_type (info, index);
+
+      if (!param_type || !AGGREGATE_TYPE_P (param_type))
+	return -1;
+    }
+  else if (TREE_THIS_VOLATILE (rhs))
+    return -1;
+
+  return index;
+}
+
 /* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
    of an assignment statement STMT, try to determine whether we are actually
    handling any of the following cases and construct an appropriate jump
@@ -1440,11 +1528,11 @@  type_like_member_ptr_p (tree type, tree *method_ptr, tree *delta)
 }
 
 /* If RHS is an SSA_NAME and it is defined by a simple copy assign statement,
-   return the rhs of its defining statement.  Otherwise return RHS as it
-   is.  */
+   return the rhs of its defining statement, and this statement is stored in
+   *RHS_STMT.  Otherwise return RHS as it is.  */
 
 static inline tree
-get_ssa_def_if_simple_copy (tree rhs)
+get_ssa_def_if_simple_copy (tree rhs, gimple **rhs_stmt)
 {
   while (TREE_CODE (rhs) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (rhs))
     {
@@ -1454,25 +1542,31 @@  get_ssa_def_if_simple_copy (tree rhs)
 	rhs = gimple_assign_rhs1 (def_stmt);
       else
 	break;
+      *rhs_stmt = def_stmt;
     }
   return rhs;
 }
 
-/* Simple linked list, describing known contents of an aggregate before
-   call.  */
+/* Simple linked list, describing contents of an aggregate before call.  */
 
 struct ipa_known_agg_contents_list
 {
   /* Offset and size of the described part of the aggregate.  */
   HOST_WIDE_INT offset, size;
-  /* Known constant value or NULL if the contents is known to be unknown.  */
-  tree constant;
+
+  /* Type of the described part of the aggregate.  */
+  tree type;
+
+  /* Known constant value or jump function data describing contents.  */
+  struct ipa_load_agg_data value;
+
   /* Pointer to the next structure in the list.  */
   struct ipa_known_agg_contents_list *next;
 };
 
-/* Add a known content item into a linked list of ipa_known_agg_contents_list
-   structure, in which all elements are sorted ascendingly by offset.  */
+/* Add an aggregate content item into a linked list of
+   ipa_known_agg_contents_list structure, in which all elements
+   are sorted ascendingly by offset.  */
 
 static inline void
 add_to_agg_contents_list (struct ipa_known_agg_contents_list **plist,
@@ -1492,7 +1586,7 @@  add_to_agg_contents_list (struct ipa_known_agg_contents_list **plist,
   *plist = item;
 }
 
-/* Check whether a given known content is clobbered by certain element in
+/* Check whether a given aggregate content is clobbered by certain element in
    a linked list of ipa_known_agg_contents_list.  */
 
 static inline bool
@@ -1512,27 +1606,189 @@  clobber_by_agg_contents_list_p (struct ipa_known_agg_contents_list *list,
 }
 
 /* Build aggregate jump function from LIST, assuming there are exactly
-   CONST_COUNT constant entries there and that offset of the passed argument
+   VALUE_COUNT entries there and that offset of the passed argument
    is ARG_OFFSET and store it into JFUNC.  */
 
 static void
 build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list,
-			       int const_count, HOST_WIDE_INT arg_offset,
+			       int value_count, HOST_WIDE_INT arg_offset,
 			       struct ipa_jump_func *jfunc)
 {
-  vec_alloc (jfunc->agg.items, const_count);
-  while (list)
+  vec_alloc (jfunc->agg.items, value_count);
+  for (; list; list = list->next)
+    {
+      struct ipa_agg_jf_item item;
+      tree operand = list->value.pass_through.operand;
+
+      if (list->value.pass_through.formal_id >= 0)
+	{
+	  /* Content value is derived from some formal paramerter.  */
+	  if (list->value.offset >= 0)
+	    item.jftype = IPA_JF_LOAD_AGG;
+	  else
+	    item.jftype = IPA_JF_PASS_THROUGH;
+
+	  item.value.load_agg = list->value;
+	  if (operand)
+	    item.value.pass_through.operand
+				= unshare_expr_without_location (operand);
+	}
+      else if (operand)
+	{
+	  /* Content value is known constant.  */
+	  item.jftype = IPA_JF_CONST;
+	  item.value.constant = unshare_expr_without_location (operand);
+	}
+      else
+	continue;
+
+      item.type = list->type;
+      gcc_assert (tree_to_shwi (TYPE_SIZE (list->type)) == list->size);
+
+      item.offset = list->offset - arg_offset;
+      gcc_assert ((item.offset % BITS_PER_UNIT) == 0);
+
+      jfunc->agg.items->quick_push (item);
+    }
+}
+
+/* Given an assignment statement STMT, try to collect information into
+   AGG_VALUE that will be used to construct jump function for RHS of the
+   assignment, from which content value of an aggregate part comes.
+
+   Besides constant and simple pass-through jump functions, also try to
+   identify whether it matches the following pattern that can be described by
+   a load-value-from-aggregate jump function, which is a derivative of simple
+   pass-through jump function.
+
+     foo (int *p)
+     {
+       ...
+
+       *(q_5 + 4) = *(p_3(D) + 28) op 1;
+       bar (q_5);
+     }
+
+   Since load-value-from-aggregate jump function data structure is informative
+   enough to describe constant and simple pass-through jump function, here we
+   do not need a jump function type, merely use FORMAL_ID and OPERAND in
+   IPA_LOAD_AGG_DATA to distinguish different jump functions.  */
+
+static void
+compute_assign_agg_jump_func (struct ipa_func_body_info *fbi,
+			      struct ipa_load_agg_data *agg_value,
+			      gimple *stmt)
+{
+  tree lhs = gimple_assign_lhs (stmt);
+  tree rhs1 = gimple_assign_rhs1 (stmt);
+  enum tree_code code;
+  int index = -1;
+
+  /* Initialize jump function data for the aggregate part.  */
+  memset (agg_value, 0, sizeof (*agg_value));
+  agg_value->pass_through.operation = NOP_EXPR;
+  agg_value->pass_through.formal_id = -1;
+  agg_value->offset = -1;
+
+  if (AGGREGATE_TYPE_P (TREE_TYPE (lhs))  /* TODO: Support aggregate type.  */
+      || TREE_THIS_VOLATILE (lhs)
+      || TREE_CODE (lhs) == BIT_FIELD_REF
+      || contains_bitfld_component_ref_p (lhs))
+    return;
+
+  /* Skip SSA copies.  */
+  while (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS)
+    {
+      if (TREE_CODE (rhs1) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (rhs1))
+	break;
+
+      if (!is_gimple_assign (stmt = SSA_NAME_DEF_STMT (rhs1)))
+	return;
+
+      rhs1 = gimple_assign_rhs1 (stmt);
+    }
+
+  code = gimple_assign_rhs_code (stmt);
+  switch (gimple_assign_rhs_class (stmt))
     {
-      if (list->constant)
+    case GIMPLE_SINGLE_RHS:
+      if (is_gimple_ip_invariant (rhs1))
 	{
-	  struct ipa_agg_jf_item item;
-	  item.offset = list->offset - arg_offset;
-	  gcc_assert ((item.offset % BITS_PER_UNIT) == 0);
-	  item.value = unshare_expr_without_location (list->constant);
-	  jfunc->agg.items->quick_push (item);
+	  agg_value->pass_through.operand = rhs1;
+	  return;
 	}
-      list = list->next;
+      code = NOP_EXPR;
+      break;
+
+    case GIMPLE_UNARY_RHS:
+      /* NOTE: A GIMPLE_UNARY_RHS operation might not be tcc_unary
+	 (truth_not_expr is example), GIMPLE_BINARY_RHS does not imply
+	 tcc_binary, this subtleness is somewhat misleading.
+
+	 Since tcc_unary is widely used in IPA-CP code to check an operation
+	 with one operand, here we only allow tc_unary operation to avoid
+	 possible problem.  Then we can use (opclass == tc_unary) or not to
+	 distinguish unary and binary.  */
+      if (TREE_CODE_CLASS (code) != tcc_unary || CONVERT_EXPR_CODE_P (code))
+	return;
+
+      rhs1 = get_ssa_def_if_simple_copy (rhs1, &stmt);
+      break;
+
+    case GIMPLE_BINARY_RHS:
+      {
+	gimple *rhs1_stmt = stmt;
+	gimple *rhs2_stmt = stmt;
+	tree rhs2 = gimple_assign_rhs2 (stmt);
+
+	rhs1 = get_ssa_def_if_simple_copy (rhs1, &rhs1_stmt);
+	rhs2 = get_ssa_def_if_simple_copy (rhs2, &rhs2_stmt);
+
+	if (is_gimple_ip_invariant (rhs2))
+	  {
+	    agg_value->pass_through.operand = rhs2;
+	    stmt = rhs1_stmt;
+	  }
+	else if (is_gimple_ip_invariant (rhs1))
+	  {
+	    if (TREE_CODE_CLASS (code) == tcc_comparison)
+	      code = swap_tree_comparison (code);
+	    else if (!commutative_tree_code (code))
+	      return;
+
+	    agg_value->pass_through.operand = rhs1;
+	    stmt = rhs2_stmt;
+	    rhs1 = rhs2;
+	  }
+	else
+	  return;
+
+	if (TREE_CODE_CLASS (code) != tcc_comparison
+	    && !useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs1)))
+	  return;
+      }
+      break;
+
+    default:
+      return;
+  }
+
+  if (TREE_CODE (rhs1) != SSA_NAME)
+    index = load_from_unmodified_param_or_agg (fbi, fbi->info, stmt,
+					       &agg_value->offset,
+					       &agg_value->by_ref);
+  else if (SSA_NAME_IS_DEFAULT_DEF (rhs1))
+    index = ipa_get_param_decl_index (fbi->info, SSA_NAME_VAR (rhs1));
+
+  if (index >= 0)
+    {
+      if (agg_value->offset >= 0)
+	agg_value->type = TREE_TYPE (rhs1);
+      agg_value->pass_through.formal_id = index;
+      agg_value->pass_through.operation = code;
     }
+  else
+    agg_value->pass_through.operand = NULL_TREE;
 }
 
 /* If STMT is a memory store to the object whose address is BASE, extract
@@ -1542,26 +1798,19 @@  build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list,
    is expected to be in form of MEM_REF expression.  */
 
 static bool
-extract_mem_content (gimple *stmt, tree base, bool check_ref,
+extract_mem_content (struct ipa_func_body_info *fbi,
+		     gimple *stmt, tree base, bool check_ref,
 		     struct ipa_known_agg_contents_list *content)
 {
   HOST_WIDE_INT lhs_offset, lhs_size;
-  tree lhs, rhs, lhs_base;
   bool reverse;
 
-  if (!gimple_assign_single_p (stmt))
-    return false;
-
-  lhs = gimple_assign_lhs (stmt);
-  rhs = gimple_assign_rhs1 (stmt);
-
-  if (!is_gimple_reg_type (TREE_TYPE (rhs))
-      || TREE_CODE (lhs) == BIT_FIELD_REF
-      || contains_bitfld_component_ref_p (lhs))
+  if (!is_gimple_assign (stmt))
     return false;
 
-  lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset,
-					  &lhs_size, &reverse);
+  tree lhs = gimple_assign_lhs (stmt);
+  tree lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset, &lhs_size,
+					       &reverse);
   if (!lhs_base)
     return false;
 
@@ -1575,32 +1824,31 @@  extract_mem_content (gimple *stmt, tree base, bool check_ref,
   else if (lhs_base != base)
     return false;
 
-  rhs = get_ssa_def_if_simple_copy (rhs);
-
-  content->size = lhs_size;
   content->offset = lhs_offset;
-  content->constant = is_gimple_ip_invariant (rhs) ? rhs : NULL_TREE;
+  content->size = lhs_size;
+  content->type = TREE_TYPE (lhs);
   content->next = NULL;
 
+  compute_assign_agg_jump_func (fbi, &content->value, stmt);
   return true;
 }
 
 /* Traverse statements from CALL backwards, scanning whether an aggregate given
-   in ARG is filled in with constant values.  ARG can either be an aggregate
-   expression or a pointer to an aggregate.  ARG_TYPE is the type of the
-   aggregate.  JFUNC is the jump function into which the constants are
-   subsequently stored.  AA_WALK_BUDGET_P points to limit on number of
-   statements we allow get_continuation_for_phi to examine.  */
+   in ARG is filled in constant or value that is derived from caller's formal
+   parameter in the way described by some kind of jump function.  FBI is the
+   context of the caller function for interprocedural analysis.  ARG can either
+   be an aggregate expression or a pointer to an aggregate.  ARG_TYPE is the
+   type of the aggregate.  JFUNC is the jump function for the aggregate.  */
 
 static void
-determine_known_aggregate_parts (gcall *call, tree arg,
+determine_known_aggregate_parts (struct ipa_func_body_info *fbi,
+				 gcall *call, tree arg,
 				 tree arg_type,
-				 struct ipa_jump_func *jfunc,
-				 unsigned *aa_walk_budget_p)
+				 struct ipa_jump_func *jfunc)
 {
   struct ipa_known_agg_contents_list *list = NULL, *all_list = NULL;
   bitmap visited = NULL;
-  int item_count = 0, const_count = 0;
+  int item_count = 0, value_count = 0;
   int ipa_max_agg_items = PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS);
   HOST_WIDE_INT arg_offset, arg_size;
   tree arg_base;
@@ -1679,7 +1927,7 @@  determine_known_aggregate_parts (gcall *call, tree arg,
       if (gimple_code (stmt) == GIMPLE_PHI)
 	{
 	  dom_vuse = get_continuation_for_phi (stmt, &r, true,
-					       *aa_walk_budget_p,
+					       fbi->aa_walk_budget,
 					       &visited, false, NULL, NULL);
 	  continue;
 	}
@@ -1689,12 +1937,13 @@  determine_known_aggregate_parts (gcall *call, tree arg,
 	  struct ipa_known_agg_contents_list *content
 			= XALLOCA (struct ipa_known_agg_contents_list);
 
-	  if (!extract_mem_content (stmt, arg_base, check_ref, content))
+	  if (!extract_mem_content (fbi, stmt, arg_base, check_ref, content))
 	    break;
 
 	  /* Now we get a dominating virtual operand, and need to check
 	     whether its value is clobbered any other dominating one.  */
-	  if (content->constant
+	  if ((content->value.pass_through.formal_id >= 0
+	       || content->value.pass_through.operand)
 	      && !clobber_by_agg_contents_list_p (all_list, content))
 	    {
 	      struct ipa_known_agg_contents_list *copy
@@ -1704,7 +1953,7 @@  determine_known_aggregate_parts (gcall *call, tree arg,
 		 operands, whose definitions can finally reach the call.  */
 	      add_to_agg_contents_list (&list, (*copy = *content, copy));
 
-	      if (++const_count == ipa_max_agg_items)
+	      if (++value_count == ipa_max_agg_items)
 		break;
 	    }
 
@@ -1722,12 +1971,12 @@  determine_known_aggregate_parts (gcall *call, tree arg,
 
   /* Third stage just goes over the list and creates an appropriate vector of
      ipa_agg_jf_item structures out of it, of course only if there are
-     any known constants to begin with.  */
+     any meaningful items to begin with.  */
 
-  if (const_count)
+  if (value_count)
     {
       jfunc->agg.by_ref = by_ref;
-      build_agg_jump_func_from_list (list, const_count, arg_offset, jfunc);
+      build_agg_jump_func_from_list (list, value_count, arg_offset, jfunc);
     }
 }
 
@@ -2019,8 +2268,7 @@  ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi,
 	      || !ipa_get_jf_ancestor_agg_preserved (jfunc))
 	  && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
 	      || POINTER_TYPE_P (param_type)))
-	determine_known_aggregate_parts (call, arg, param_type, jfunc,
-					 &fbi->aa_walk_budget);
+	determine_known_aggregate_parts (fbi, call, arg, param_type, jfunc);
     }
   if (!useful_context)
     vec_free (args->polymorphic_call_contexts);
@@ -2679,6 +2927,72 @@  update_jump_functions_after_inlining (struct cgraph_edge *cs,
       class ipa_polymorphic_call_context *dst_ctx
 	= ipa_get_ith_polymorhic_call_context (args, i);
 
+      if (dst->agg.items)
+	{
+	  struct ipa_agg_jf_item *item;
+	  int j;
+
+	  FOR_EACH_VEC_ELT (*dst->agg.items, j, item)
+	    {
+	      int dst_fid;
+	      struct ipa_jump_func *src;
+
+	      if (item->jftype != IPA_JF_PASS_THROUGH
+		  && item->jftype != IPA_JF_LOAD_AGG)
+		continue;
+
+	      dst_fid = item->value.pass_through.formal_id;
+	      if (dst_fid >= ipa_get_cs_argument_count (top))
+		{
+		  item->jftype = IPA_JF_UNKNOWN;
+		  continue;
+		}
+
+	      item->value.pass_through.formal_id = -1;
+	      src = ipa_get_ith_jump_func (top, dst_fid);
+	      if (src->type == IPA_JF_CONST)
+		{
+		  if (item->jftype == IPA_JF_PASS_THROUGH
+		      && item->value.pass_through.operation == NOP_EXPR)
+		    {
+		      item->jftype = IPA_JF_CONST;
+		      item->value.constant = src->value.constant.value;
+		      continue;
+		    }
+		}
+	      else if (src->type == IPA_JF_PASS_THROUGH
+		       && src->value.pass_through.operation == NOP_EXPR)
+		{
+		  if (item->jftype == IPA_JF_PASS_THROUGH
+		      || !item->value.load_agg.by_ref
+		      || src->value.pass_through.agg_preserved)
+		    item->value.pass_through.formal_id
+				= src->value.pass_through.formal_id;
+		}
+	      else if (src->type == IPA_JF_ANCESTOR)
+		{
+		  if (item->jftype == IPA_JF_PASS_THROUGH)
+		    {
+		      if (!src->value.ancestor.offset)
+			item->value.pass_through.formal_id
+				= src->value.ancestor.formal_id;
+		    }
+		  else if (src->value.ancestor.agg_preserved)
+		    {
+		      gcc_checking_assert (item->value.load_agg.by_ref);
+
+		      item->value.pass_through.formal_id
+				 = src->value.ancestor.formal_id;
+		      item->value.load_agg.offset
+				+= src->value.ancestor.offset;
+		    }
+		}
+
+	      if (item->value.pass_through.formal_id < 0)
+		item->jftype = IPA_JF_UNKNOWN;
+	    }
+	}
+
       if (dst->type == IPA_JF_ANCESTOR)
 	{
 	  struct ipa_jump_func *src;
@@ -2718,8 +3032,11 @@  update_jump_functions_after_inlining (struct cgraph_edge *cs,
 		}
 	    }
 
-	  if (src->agg.items
-	      && (dst->value.ancestor.agg_preserved || !src->agg.by_ref))
+	  /* Parameter and argument in ancestor jump function must be pointer
+	     type, which means access to aggregate must be by-reference.  */
+	  gcc_checking_assert (!src->agg.items || src->agg.by_ref);
+
+	  if (src->agg.items && dst->value.ancestor.agg_preserved)
 	    {
 	      struct ipa_agg_jf_item *item;
 	      int j;
@@ -3111,18 +3428,19 @@  ipa_find_agg_cst_from_init (tree scalar, HOST_WIDE_INT offset, bool by_ref)
   return find_constructor_constant_at_offset (DECL_INITIAL (scalar), offset);
 }
 
-/* Retrieve value from aggregate jump function AGG or static initializer of
-   SCALAR (which can be NULL) for the given OFFSET or return NULL if there is
-   none.  BY_REF specifies whether the value has to be passed by reference or
-   by value.  If FROM_GLOBAL_CONSTANT is non-NULL, then the boolean it points
-   to is set to true if the value comes from an initializer of a constant.  */
+/* Retrieve value from AGG, a set of known offset/value for an aggregate or
+   static initializer of SCALAR (which can be NULL) for the given OFFSET or
+   return NULL if there is none.  BY_REF specifies whether the value has to be
+   passed by reference or by value.  If FROM_GLOBAL_CONSTANT is non-NULL, then
+   the boolean it points to is set to true if the value comes from an
+   initializer of a constant.  */
 
 tree
-ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
+ipa_find_agg_cst_for_param (struct ipa_agg_value_set *agg, tree scalar,
 			    HOST_WIDE_INT offset, bool by_ref,
 			    bool *from_global_constant)
 {
-  struct ipa_agg_jf_item *item;
+  struct ipa_agg_value *item;
   int i;
 
   if (scalar)
@@ -3140,7 +3458,7 @@  ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
       || by_ref != agg->by_ref)
     return NULL;
 
-  FOR_EACH_VEC_SAFE_ELT (agg->items, i, item)
+  FOR_EACH_VEC_ELT (agg->items, i, item)
     if (item->offset == offset)
       {
 	/* Currently we do not have clobber values, return NULL for them once
@@ -3236,11 +3554,13 @@  try_decrement_rdesc_refcount (struct ipa_jump_func *jfunc)
    pointer formal parameter described by jump function JFUNC.  TARGET_TYPE is
    the type of the parameter to which the result of JFUNC is passed.  If it can
    be determined, return the newly direct edge, otherwise return NULL.
-   NEW_ROOT_INFO is the node info that JFUNC lattices are relative to.  */
+   NEW_ROOT and NEW_ROOT_INFO is the node and its info that JFUNC lattices are
+   relative to.  */
 
 static struct cgraph_edge *
 try_make_edge_direct_simple_call (struct cgraph_edge *ie,
 				  struct ipa_jump_func *jfunc, tree target_type,
+				  struct cgraph_node *new_root,
 				  class ipa_node_params *new_root_info)
 {
   struct cgraph_edge *cs;
@@ -3250,10 +3570,14 @@  try_make_edge_direct_simple_call (struct cgraph_edge *ie,
   if (agg_contents)
     {
       bool from_global_constant;
-      target = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
+      ipa_agg_value_set agg = ipa_agg_value_set_from_jfunc (new_root_info,
+							    new_root,
+							    &jfunc->agg);
+      target = ipa_find_agg_cst_for_param (&agg, scalar,
 					   ie->indirect_info->offset,
 					   ie->indirect_info->by_ref,
 					   &from_global_constant);
+      agg.release ();
       if (target
 	  && !from_global_constant
 	  && !ie->indirect_info->guaranteed_unmodified)
@@ -3307,12 +3631,16 @@  ipa_impossible_devirt_target (struct cgraph_edge *ie, tree target)
    call based on a formal parameter which is described by jump function JFUNC
    and if it can be determined, make it direct and return the direct edge.
    Otherwise, return NULL.  CTX describes the polymorphic context that the
-   parameter the call is based on brings along with it.  */
+   parameter the call is based on brings along with it.  NEW_ROOT and
+   NEW_ROOT_INFO is the node and its info that JFUNC lattices are relative
+   to.  */
 
 static struct cgraph_edge *
 try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
 				   struct ipa_jump_func *jfunc,
-				   class ipa_polymorphic_call_context ctx)
+				   class ipa_polymorphic_call_context ctx,
+				   struct cgraph_node *new_root,
+				   class ipa_node_params *new_root_info)
 {
   tree target = NULL;
   bool speculative = false;
@@ -3330,9 +3658,13 @@  try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
       unsigned HOST_WIDE_INT offset;
       tree scalar = (jfunc->type == IPA_JF_CONST) ? ipa_get_jf_constant (jfunc)
 	: NULL;
-      tree t = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
+      ipa_agg_value_set agg = ipa_agg_value_set_from_jfunc (new_root_info,
+							    new_root,
+							    &jfunc->agg);
+      tree t = ipa_find_agg_cst_for_param (&agg, scalar,
 					   ie->indirect_info->offset,
 					   true);
+      agg.release ();
       if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset))
 	{
 	  bool can_refer;
@@ -3423,14 +3755,15 @@  update_indirect_edges_after_inlining (struct cgraph_edge *cs,
 {
   class ipa_edge_args *top;
   struct cgraph_edge *ie, *next_ie, *new_direct_edge;
+  struct cgraph_node *new_root;
   class ipa_node_params *new_root_info, *inlined_node_info;
   bool res = false;
 
   ipa_check_create_edge_args ();
   top = IPA_EDGE_REF (cs);
-  new_root_info = IPA_NODE_REF (cs->caller->inlined_to
-				? cs->caller->inlined_to
-				: cs->caller);
+  new_root = cs->caller->inlined_to
+		? cs->caller->inlined_to : cs->caller;
+  new_root_info = IPA_NODE_REF (new_root);
   inlined_node_info = IPA_NODE_REF (cs->callee->function_symbol ());
 
   for (ie = node->indirect_calls; ie; ie = next_ie)
@@ -3469,13 +3802,16 @@  update_indirect_edges_after_inlining (struct cgraph_edge *cs,
 	{
           ipa_polymorphic_call_context ctx;
 	  ctx = ipa_context_from_jfunc (new_root_info, cs, param_index, jfunc);
-	  new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc, ctx);
+	  new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc, ctx,
+							       new_root,
+							       new_root_info);
 	}
       else
 	{
 	  tree target_type =  ipa_get_type (inlined_node_info, param_index);
 	  new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc,
 							      target_type,
+							      new_root,
 							      new_root_info);
 	}
 
@@ -4171,6 +4507,8 @@  ipa_write_jump_function (struct output_block *ob,
       bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1);
       streamer_write_bitpack (&bp);
       break;
+    default:
+      fatal_error (UNKNOWN_LOCATION, "invalid jump function in LTO stream");
     }
 
   count = vec_safe_length (jump_func->agg.items);
@@ -4184,8 +4522,36 @@  ipa_write_jump_function (struct output_block *ob,
 
   FOR_EACH_VEC_SAFE_ELT (jump_func->agg.items, i, item)
     {
+      stream_write_tree (ob, item->type, true);
       streamer_write_uhwi (ob, item->offset);
-      stream_write_tree (ob, item->value, true);
+      streamer_write_uhwi (ob, item->jftype);
+      switch (item->jftype)
+	{
+	case IPA_JF_UNKNOWN:
+	  break;
+	case IPA_JF_CONST:
+	  stream_write_tree (ob, item->value.constant, true);
+	  break;
+	case IPA_JF_PASS_THROUGH:
+	case IPA_JF_LOAD_AGG:
+	  streamer_write_uhwi (ob, item->value.pass_through.operation);
+	  streamer_write_uhwi (ob, item->value.pass_through.formal_id);
+	  if (TREE_CODE_CLASS (item->value.pass_through.operation)
+							!= tcc_unary)
+	    stream_write_tree (ob, item->value.pass_through.operand, true);
+	  if (item->jftype == IPA_JF_LOAD_AGG)
+	    {
+	      stream_write_tree (ob, item->value.load_agg.type, true);
+	      streamer_write_uhwi (ob, item->value.load_agg.offset);
+	      bp = bitpack_create (ob->main_stream);
+	      bp_pack_value (&bp, item->value.load_agg.by_ref, 1);
+	      streamer_write_bitpack (&bp);
+	    }
+	  break;
+	default:
+	  fatal_error (UNKNOWN_LOCATION,
+		       "invalid jump function in LTO stream");
+	}
     }
 
   bp = bitpack_create (ob->main_stream);
@@ -4282,8 +4648,39 @@  ipa_read_jump_function (class lto_input_block *ib,
   for (i = 0; i < count; i++)
     {
       struct ipa_agg_jf_item item;
+      item.type = stream_read_tree (ib, data_in);
       item.offset = streamer_read_uhwi (ib);
-      item.value = stream_read_tree (ib, data_in);
+      item.jftype = (enum jump_func_type) streamer_read_uhwi (ib);
+
+      switch (item.jftype)
+	{
+	case IPA_JF_UNKNOWN:
+	  break;
+	case IPA_JF_CONST:
+	  item.value.constant = stream_read_tree (ib, data_in);
+	  break;
+	case IPA_JF_PASS_THROUGH:
+	case IPA_JF_LOAD_AGG:
+	  operation = (enum tree_code) streamer_read_uhwi (ib);
+	  item.value.pass_through.operation = operation;
+	  item.value.pass_through.formal_id = streamer_read_uhwi (ib);
+	  if (TREE_CODE_CLASS (operation) == tcc_unary)
+	    item.value.pass_through.operand = NULL_TREE;
+	  else
+	    item.value.pass_through.operand = stream_read_tree (ib, data_in);
+	  if (item.jftype == IPA_JF_LOAD_AGG)
+	    {
+	      struct bitpack_d bp;
+	      item.value.load_agg.type = stream_read_tree (ib, data_in);
+	      item.value.load_agg.offset = streamer_read_uhwi (ib);
+	      bp = streamer_read_bitpack (ib);
+	      item.value.load_agg.by_ref = bp_unpack_value (&bp, 1);
+	    }
+	  break;
+	default:
+	  fatal_error (UNKNOWN_LOCATION,
+		       "invalid jump function in LTO stream");
+	}
       if (prevails)
         jump_func->agg.items->quick_push (item);
     }
@@ -5325,9 +5722,9 @@  ipcp_transform_function (struct cgraph_node *node)
 }
 
 
-/* Return true if OTHER describes same agg item.  */
+/* Return true if OTHER describes same agg value.  */
 bool
-ipa_agg_jf_item::equal_to (const ipa_agg_jf_item &other)
+ipa_agg_value::equal_to (const ipa_agg_value &other)
 {
   return offset == other.offset
 	 && operand_equal_p (value, other.value, 0);
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 9f2479e7fdc..cb54d2547e1 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -39,6 +39,15 @@  along with GCC; see the file COPYING3.  If not see
                   argument.
    Unknown      - neither of the above.
 
+   IPA_JF_LOAD_AGG is a compound pass-through jump function, in which primary
+   operation on formal parameter is memory dereference that loads a value from
+   a part of an aggregate, which is represented or pointed to by the formal
+   parameter.  Moreover, an additional unary/binary operation can be applied on
+   the loaded value, and final result is passed as actual argument of callee
+   (e.g. *(param_1(D) + 4) op 24 ).  It is meant to describe usage of aggregate
+   parameter or by-reference parameter referenced in argument passing, commonly
+   found in C++ and Fortran.
+
    IPA_JF_ANCESTOR is a special pass-through jump function, which means that
    the result is an address of a part of the object pointed to by the formal
    parameter to which the function refers.  It is mainly intended to represent
@@ -60,6 +69,7 @@  enum jump_func_type
   IPA_JF_UNKNOWN = 0,  /* newly allocated and zeroed jump functions default */
   IPA_JF_CONST,             /* represented by field costant */
   IPA_JF_PASS_THROUGH,	    /* represented by field pass_through */
+  IPA_JF_LOAD_AGG,	    /* represented by field load_agg */
   IPA_JF_ANCESTOR	    /* represented by field ancestor */
 };
 
@@ -97,6 +107,26 @@  struct GTY(()) ipa_pass_through_data
   unsigned agg_preserved : 1;
 };
 
+/* Structure holding data required to describe a load-value-from-aggregate
+   jump function.  */
+
+struct GTY(()) ipa_load_agg_data
+{
+  /* Inherit from pass through jump function, describing unary/binary
+     operation on the value loaded from aggregate that is represented or
+     pointed to by the formal parameter, specified by formal_id in this
+     pass_through jump function data structure.  */
+  struct ipa_pass_through_data pass_through;
+  /* Type of the value loaded from the aggregate.  */
+  tree type;
+  /* Offset at which the value is located within the aggregate.  */
+  HOST_WIDE_INT offset;
+  /* True if loaded by reference (the aggregate is pointed to by the formal
+     parameter) or false if loaded by value (the aggregate is represented
+     by the formal parameter).  */
+  bool by_ref;
+};
+
 /* Structure holding data required to describe an ancestor pass-through
    jump function.  */
 
@@ -110,58 +140,139 @@  struct GTY(()) ipa_ancestor_jf_data
   unsigned agg_preserved : 1;
 };
 
-/* An element in an aggegate part of a jump function describing a known value
-   at a given offset.  When it is part of a pass-through jump function with
-   agg_preserved set or an ancestor jump function with agg_preserved set, all
-   unlisted positions are assumed to be preserved but the value can be a type
-   node, which means that the particular piece (starting at offset and having
-   the size of the type) is clobbered with an unknown value.  When
-   agg_preserved is false or the type of the containing jump function is
-   different, all unlisted parts are assumed to be unknown and all values must
-   fulfill is_gimple_ip_invariant.  */
+/* A jump function for an aggregate part at a given offset, which describes how
+   it content value is generated.  All unlisted positions are assumed to have a
+   value defined in an unknown way.  */
 
 struct GTY(()) ipa_agg_jf_item
 {
-  /* The offset at which the known value is located within the aggregate.  */
+  /* The offset for the aggregate part.  */
   HOST_WIDE_INT offset;
 
-  /* The known constant or type if this is a clobber.  */
-  tree value;
+  /* Data type of the aggregate part.  */
+  tree type;
 
-  /* Return true if OTHER describes same agg item.  */
-  bool equal_to (const ipa_agg_jf_item &other);
-};
+  /* Jump function type.  */
+  enum jump_func_type jftype;
 
+  /* Represents a value of jump function. constant represents the actual constant
+     in constant jump function content.  pass_through is used only in simple pass
+     through jump function context.  load_agg is for load-value-from-aggregate
+     jump function context.  */
+  union jump_func_agg_value
+  {
+    tree GTY ((tag ("IPA_JF_CONST"))) constant;
+    struct ipa_pass_through_data GTY ((tag ("IPA_JF_PASS_THROUGH"))) pass_through;
+    struct ipa_load_agg_data GTY ((tag ("IPA_JF_LOAD_AGG"))) load_agg;
+  } GTY ((desc ("%1.jftype"))) value;
+};
 
-/* Aggregate jump function - i.e. description of contents of aggregates passed
-   either by reference or value.  */
+/* Jump functions describing a set of aggregate contents.  */
 
 struct GTY(()) ipa_agg_jump_function
 {
-  /* Description of the individual items.  */
+  /* Description of the individual jump function item.  */
   vec<ipa_agg_jf_item, va_gc> *items;
-  /* True if the data was passed by reference (as opposed to by value). */
+  /* True if the data was passed by reference (as opposed to by value).  */
   bool by_ref;
+};
+
+/* An element in an aggregate part describing a known value at a given offset.
+   All unlisted positions are assumed to be unknown and all listed values must
+   fulfill is_gimple_ip_invariant.  */
+
+struct GTY(()) ipa_agg_value
+{
+  /* The offset at which the known value is located within the aggregate.  */
+  HOST_WIDE_INT offset;
 
-  /* Return true if OTHER describes same agg items.  */
-  bool equal_to (const ipa_agg_jump_function &other)
+  /* The known constant.  */
+  tree value;
+
+  /* Return true if OTHER describes same agg value.  */
+  bool equal_to (const ipa_agg_value &other);
+};
+
+/* Structure describing a set of known offset/value for aggregate.  */
+
+struct GTY(()) ipa_agg_value_set
+{
+  /* Description of the individual item.  */
+  vec<ipa_agg_value> items;
+  /* True if the data was passed by reference (as opposed to by value).  */
+  bool by_ref;
+
+  /* Return true if OTHER describes same agg values.  */
+  bool equal_to (const ipa_agg_value_set &other)
   {
     if (by_ref != other.by_ref)
       return false;
-    if (items != NULL && other.items == NULL)
-      return false;
-    if (!items)
-      return other.items == NULL;
-    if (items->length () != other.items->length ())
+    if (items.length () != other.items.length ())
       return false;
-    for (unsigned int i = 0; i < items->length (); i++)
-      if (!(*items)[i].equal_to ((*other.items)[i]))
+    for (unsigned int i = 0; i < items.length (); i++)
+      if (!items[i].equal_to (other.items[i]))
 	return false;
     return true;
   }
+
+  /* Return true if there is any value for aggregate.  */
+  operator bool () const
+  {
+    return !items.is_empty ();
+  }
+
+  ipa_agg_value_set copy () const
+  {
+    ipa_agg_value_set new_copy;
+
+    new_copy.items = items.copy ();
+    new_copy.by_ref = by_ref;
+
+    return new_copy;
+  }
+
+  void release ()
+  {
+    items.release ();
+  }
 };
 
-typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
+/* Return copy of a vec<ipa_agg_value_set>.  */
+
+static inline vec<ipa_agg_value_set>
+ipa_copy_agg_values (const vec<ipa_agg_value_set> &aggs)
+{
+  vec<ipa_agg_value_set> aggs_copy = vNULL;
+
+  if (!aggs.is_empty ())
+    {
+      ipa_agg_value_set *agg;
+      int i;
+
+      aggs_copy.reserve_exact (aggs.length ());
+
+      FOR_EACH_VEC_ELT (aggs, i, agg)
+	aggs_copy.quick_push (agg->copy ());
+    }
+
+  return aggs_copy;
+}
+
+/* For vec<ipa_agg_value_set>, DO NOT call release(), use below function
+   instead.  Because ipa_agg_value_set contains a field of vector type, we
+   should release this child vector in each element before reclaiming the
+   whole vector.  */
+
+static inline void
+ipa_release_agg_values (vec<ipa_agg_value_set> &aggs)
+{
+  ipa_agg_value_set *agg;
+  int i;
+
+  FOR_EACH_VEC_ELT (aggs, i, agg)
+    agg->release ();
+  aggs.release ();
+}
 
 /* Information about zero/non-zero bits.  */
 class GTY(()) ipa_bits
@@ -193,8 +304,8 @@  public:
    types of jump functions supported.  */
 struct GTY (()) ipa_jump_func
 {
-  /* Aggregate contants description.  See struct ipa_agg_jump_function and its
-     description.  */
+  /* Aggregate jump function description.  See struct ipa_agg_jump_function
+     and its description.  */
   struct ipa_agg_jump_function agg;
 
   /* Information about zero/non-zero bits.  The pointed to structure is shared
@@ -832,9 +943,9 @@  bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
 
 /* Indirect edge and binfo processing.  */
 tree ipa_get_indirect_edge_target (struct cgraph_edge *ie,
-				   vec<tree> ,
+				   vec<tree>,
 				   vec<ipa_polymorphic_call_context>,
-				   vec<ipa_agg_jump_function_p>,
+				   vec<ipa_agg_value_set>,
 				   bool *);
 struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
 						    bool speculative = false);
@@ -847,7 +958,7 @@  ipa_bits *ipa_get_ipa_bits_for_value (const widest_int &value,
 void ipa_analyze_node (struct cgraph_node *);
 
 /* Aggregate jump function related functions.  */
-tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
+tree ipa_find_agg_cst_for_param (struct ipa_agg_value_set *agg, tree scalar,
 				 HOST_WIDE_INT offset, bool by_ref,
 				 bool *from_global_constant = NULL);
 bool ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
@@ -893,6 +1004,9 @@  ipa_polymorphic_call_context ipa_context_from_jfunc (ipa_node_params *,
 						     cgraph_edge *,
 						     int,
 						     ipa_jump_func *);
+ipa_agg_value_set ipa_agg_value_set_from_jfunc (ipa_node_params *,
+						cgraph_node *,
+						ipa_agg_jump_function *);
 void ipa_dump_param (FILE *, class ipa_node_params *info, int i);
 void ipa_release_body_info (struct ipa_func_body_info *);
 tree ipa_get_callee_param_type (struct cgraph_edge *e, int i);
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c
index 16d62e72c9a..c61e96a842b 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c
@@ -72,7 +72,7 @@  int caller2(void)
   return sum;
 }
 
-/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 1" 1 "cp" } } */
-/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 2" 1 "cp" } } */
-/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 3" 1 "cp" } } */
-/* { dg-final { scan-ipa-dump-times "offset: 64, cst: 4" 1 "cp" } } */
+/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 1" 1 "cp" } } */
+/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 2" 1 "cp" } } */
+/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 3" 1 "cp" } } */
+/* { dg-final { scan-ipa-dump-times "offset: 64, type: int, CONST: 4" 1 "cp" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
new file mode 100644
index 00000000000..3c496eeef39
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
@@ -0,0 +1,77 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fno-early-inlining" } */
+/* { dg-add-options bind_pic_locally } */
+
+struct S
+{
+  int a, b, c;
+};
+
+void *blah(int, void *);
+
+#define foo_body(p)\
+{ \
+  int i, c = (p)->c; \
+  int b = (p)->b; \
+  void *v = (void *) (p); \
+ \
+  for (i= 0; i< c; i++) \
+    v = blah(b + i, v); \
+}
+
+static void __attribute__ ((noinline))
+foo_v (struct S s)
+{
+  foo_body (&s);
+}
+
+static void __attribute__ ((noinline))
+foo_r (struct S *p)
+{
+  foo_body (p);
+}
+
+static void
+goo_v (int a, int *p)
+{
+  struct S s;
+  s.a = 101;
+  s.b = a % 7;
+  s.c = *p + 6;
+  foo_v (s);
+}
+
+static void
+goo_r (int a, struct S n)
+{
+  struct S s;
+  s.a = 1;
+  s.b = a + 5;
+  s.c = -n.b;
+  foo_r (&s);
+}
+
+void
+entry ()
+{
+  int a;
+  int v;
+  struct S s;
+
+  a = 9;
+  v = 3;
+  goo_v (a, &v);
+
+  a = 100;
+  s.b = 18;
+  goo_r (a, s);
+}
+
+/* { dg-final { scan-ipa-dump "offset: 0, type: int, CONST: 1" "cp" } } */
+/* { dg-final { scan-ipa-dump "offset: 32, type: int, PASS THROUGH: 0, op plus_expr 5" "cp" } } */
+/* { dg-final { scan-ipa-dump "offset: 64, type: int, LOAD AGG: 1 \\\[offset: 32, by value], op negate_expr" "cp" } } */
+/* { dg-final { scan-ipa-dump "offset: 0, type: int, CONST: 101" "cp" } } */
+/* { dg-final { scan-ipa-dump "offset: 32, type: int, PASS THROUGH: 0, op trunc_mod_expr 7" "cp" } } */
+/* { dg-final { scan-ipa-dump "offset: 64, type: int, LOAD AGG: 1 \\\[offset: 0, by reference], op plus_expr 6" "cp" } } */
+/* { dg-final { scan-ipa-dump "Aggregate replacements: 0\\\[0]=1, 0\\\[32]=105, 0\\\[64]=-18" "cp" } } */
+/* { dg-final { scan-ipa-dump "Aggregate replacements: 0\\\[0]=101, 0\\\[32]=2, 0\\\[64]=9" "cp" } } */
-- 
2.17.1