public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682))
@ 2019-09-06 12:35 Feng Xue OS
  2019-09-06 16:39 ` [PATCH V2] " Feng Xue OS
  0 siblings, 1 reply; 28+ messages in thread
From: Feng Xue OS @ 2019-09-06 12:35 UTC (permalink / raw)
  To: Martin Jambor, Jan Hubicka, gcc-patches

Current IPA only supports a simple kind of CP on by-ref argument, that is directly defined with a constant value before callsite. This patch is meant to extend CP to handle a more generalized form on by-ref argument definition, which is similar to what have done on by-value argument. It will cover the following patterns. ("arg" stands for callee actual argument, and param is caller formal parameter.)
  
    *arg = unary-op  param
    *arg = unary-op *param
    *arg =  param binary-op constant
    *arg = *param binary-op constant

Feng
------
index 33d52fe5537..b0ff48d130e 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1244,23 +1244,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)
@@ -1274,8 +1274,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;
@@ -1283,6 +1282,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.  */
 
@@ -1416,6 +1430,150 @@ 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 load size is same as source value size.  */
+      if (TYPE_SIZE (load_type) != TYPE_SIZE (value_type))
+	return NULL_TREE;
+
+      /* 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.  */
@@ -1592,16 +1750,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;
@@ -1611,17 +1778,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 ();
       }
@@ -1629,6 +1801,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.  */
@@ -1789,7 +1979,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
 	      added_sth = true;
 	    }
 	}
-
     }
 
  prop_fail:
@@ -2145,6 +2334,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.  */
 
@@ -2212,15 +2480,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)
@@ -2326,7 +2593,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)
 {
@@ -2364,9 +2631,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;
@@ -2420,8 +2687,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);
     }
@@ -2543,7 +2809,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,
@@ -2557,7 +2823,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;
@@ -2691,25 +2957,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;
 }
@@ -2725,7 +2991,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);
@@ -2775,40 +3041,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
@@ -2818,7 +3064,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)
 {
@@ -2827,7 +3073,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)
@@ -2841,7 +3087,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;
 
@@ -2867,8 +3113,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;
 
@@ -2881,9 +3126,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->local.can_change_signature))
     {
@@ -2896,7 +3140,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);
@@ -2959,7 +3203,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))
@@ -2994,7 +3238,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))
@@ -3013,13 +3257,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;
@@ -3031,14 +3275,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))
@@ -3054,18 +3298,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);
 }
 
 
@@ -3433,26 +3673,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
@@ -4074,10 +4294,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;
@@ -4085,7 +4305,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);
@@ -4098,11 +4318,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)
@@ -4140,18 +4360,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;
@@ -4167,11 +4387,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);
@@ -4208,9 +4428,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);
@@ -4291,12 +4511,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)
 	  {
@@ -4314,9 +4548,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;
 		  }
@@ -4329,7 +4564,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
   else
     {
       inter.release ();
-      return vec<ipa_agg_jf_item>();
+      return vNULL;
     }
   return inter;
 }
@@ -4357,8 +4592,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;
 
@@ -4465,7 +4700,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)
@@ -4488,7 +4723,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)
@@ -4726,7 +4961,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)
@@ -4737,8 +4971,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++)
     {
@@ -4807,9 +5040,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 278bf606661..413ba219bbf 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -306,9 +306,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.  */
 
@@ -316,8 +316,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)
 {
@@ -347,7 +346,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
@@ -356,7 +355,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);
 	    }
@@ -420,12 +419,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;
 
   if (clause_ptr)
     *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
@@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
       && !e->call_stmt_cannot_inline_p
       && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
     {
+      struct cgraph_node *caller;
       class ipa_node_params *caller_parms_info, *callee_pi;
       class ipa_edge_args *args = IPA_EDGE_REF (e);
       class ipa_call_summary *es = ipa_call_summaries->get (e);
       int i, count = ipa_get_cs_argument_count (args);
 
       if (e->caller->global.inlined_to)
-	caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
+	caller = e->caller->global.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))
@@ -481,10 +482,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
@@ -516,7 +516,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);
 }
 
 
@@ -2499,7 +2499,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;
@@ -2548,7 +2548,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);
@@ -2583,7 +2583,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)
@@ -2646,7 +2646,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
 			     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,
 			     int *ret_size, int *ret_min_size,
 			     sreal *ret_time,
 			     sreal *ret_nonspecialized_time,
@@ -2782,7 +2782,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 173d3f2a652..7e561dab400 100644
--- a/gcc/ipa-fnsummary.h
+++ b/gcc/ipa-fnsummary.h
@@ -260,7 +260,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);
@@ -274,13 +274,13 @@ void 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> *);
+				   vec<ipa_agg_value_set> *);
 void estimate_node_size_and_time (struct cgraph_node *node,
 				  clause_t possible_truths,
 				  clause_t nonspec_possible_truths,
 				  vec<tree> known_vals,
 				  vec<ipa_polymorphic_call_context>,
-				  vec<ipa_agg_jump_function_p> known_aggs,
+				  vec<ipa_agg_value_set> known_aggs,
 				  int *ret_size, int *ret_min_size,
 				  sreal *ret_time,
 				  sreal *ret_nonspecialized_time,
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index a66af277d03..bf4e6ea3392 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -127,7 +127,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;
 
@@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
 
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   gcc_checking_assert (size >= 0);
   gcc_checking_assert (time >= 0);
 
@@ -186,7 +186,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.  */
 
@@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
 			       NULL, NULL, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   return size;
 }
 
@@ -227,7 +227,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.  */
 
@@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
 			       NULL, NULL, &hints, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   hints |= simple_edge_hints (edge);
   return hints;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index a23aa2590a0..eb9466c92fc 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -359,18 +359,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");
 	    }
 	}
@@ -870,7 +897,7 @@ find_dominating_aa_status (struct ipa_func_body_info *fbi, basic_block bb,
 }
 
 /* Get AA status structure for the given BB and parameter with INDEX.  Allocate
-   structures and/or intialize the result with a dominating description as
+   structures and/or initialize the result with a dominating description as
    necessary.  */
 
 static struct ipa_param_aa_status *
@@ -1135,6 +1162,64 @@ 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 involving 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;
+
+  if (!ipa_load_from_parm_agg (fbi, info->descriptors, stmt, rhs, &index,
+			       offset_p, &size, by_ref_p))
+    return -1;
+
+  /* Skip bit-field whose size does not equal any integral type or offset
+     is not byte-aligned.  */
+  if (maybe_ne (tree_to_poly_int64 (TYPE_SIZE (TREE_TYPE (rhs))), size)
+      || *offset_p % BITS_PER_UNIT)
+    return -1;
+
+  if (!*by_ref_p)
+    {
+      tree param_type = ipa_get_type (info, index);
+
+      if (!param_type || !AGGREGATE_TYPE_P (param_type))
+        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
@@ -1438,11 +1523,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))
     {
@@ -1452,25 +1537,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,
@@ -1490,7 +1581,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
@@ -1510,27 +1601,175 @@ 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;
+      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 disginguish 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;
+
+  /* Only scalar type value is meaningful for aggregate part.  */
+  gcc_checking_assert (!AGGREGATE_TYPE_P (TREE_TYPE (lhs)));
+
+  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;
+
+  /* Skip SSA copies.  */
+  while (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS)
     {
-      if (list->constant)
+      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))
+    {
+    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:
+      if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs1)))
+	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
@@ -1540,14 +1779,15 @@ 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))
+  if (!is_gimple_assign (stmt))
     return false;
 
   lhs = gimple_assign_lhs (stmt);
@@ -1573,32 +1813,33 @@ 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);
+  gcc_assert (tree_to_shwi (TYPE_SIZE (TREE_TYPE (lhs))) == lhs_size);
 
-  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;
@@ -1677,7 +1918,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;
 	}
@@ -1687,12 +1928,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
@@ -1702,7 +1944,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;
 	    }
 
@@ -1720,12 +1962,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);
     }
 }
 
@@ -2017,8 +2259,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);
@@ -2661,6 +2902,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;
@@ -3093,18 +3400,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)
@@ -3122,7 +3430,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
@@ -3218,11 +3526,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;
@@ -3232,10 +3542,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)
@@ -3289,12 +3603,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;
@@ -3312,9 +3630,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;
@@ -3405,14 +3727,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->global.inlined_to
-				? cs->caller->global.inlined_to
-				: cs->caller);
+  new_root = cs->caller->global.inlined_to
+		? cs->caller->global.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)
@@ -3451,13 +3774,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);
 	}
 
@@ -4125,6 +4451,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);
@@ -4138,8 +4466,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_binary)
+	    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);
@@ -4236,8 +4592,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_binary)
+	    item.value.pass_through.operand = stream_read_tree (ib, data_in);
+	  else
+	    item.value.pass_through.operand = NULL_TREE;
+          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);
     }
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 30948fb8854..20c737fb3f5 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 one
+   primary operation on formal parameter is memory dereference that loads
+   a value from a part of an aggregate data, which is represented or pointed
+   by the formal parameter. Moreover, one 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 or by-reference parameter during argument passing 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
+{
+  /* Derive from pass through jump function, describing unary/binary
+     operation on the value loaded from aggregate that is represented
+     or pointed by the formal parameter specified by formal_id in this
+     pass through jump function.  */
+  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 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,38 +140,86 @@ 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;
+
+  /* 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). */
   bool by_ref;
 };
 
-typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
+/* 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;
+
+  /* The known constant.  */
+  tree value;
+};
+
+/* 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;
+
+  void release ()
+  {
+    items.release ();
+  }
+};
+
+/* 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
@@ -172,8 +250,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
@@ -199,7 +277,7 @@ struct GTY (()) ipa_jump_func
 };
 
 
-/* Return the constant stored in a constant jump functin JFUNC.  */
+/* Return the constant stored in a constant jump function JFUNC.  */
 
 static inline tree
 ipa_get_jf_constant (struct ipa_jump_func *jfunc)
@@ -290,7 +368,7 @@ ipa_get_jf_ancestor_agg_preserved (struct ipa_jump_func *jfunc)
   return jfunc->value.ancestor.agg_preserved;
 }
 
-/* Return true if ancestor jump function JFUNC presrves type information.  */
+/* Return true if ancestor jump function JFUNC preserves type information.  */
 
 static inline bool
 ipa_get_jf_ancestor_type_preserved (struct ipa_jump_func *jfunc)
@@ -742,9 +820,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);
@@ -757,7 +835,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,
@@ -803,6 +881,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..67e0b3a9a93
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
@@ -0,0 +1,78 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fdump-tree-optimized-slim -fno-early-inlining"  } */
+/* { dg-add-options bind_pic_locally } */
+
+struct S
+{
+  int a, b, c;
+};
+
+void *blah(int, void *);
+
+static void inline
+foo (struct S *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 (&s);
+}
+
+static void __attribute__ ((noinline))
+foo_r (struct S *p)
+{
+  foo (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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH V2] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682))
  2019-09-06 12:35 [PATCH] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)) Feng Xue OS
@ 2019-09-06 16:39 ` Feng Xue OS
  2019-09-09  8:40   ` [PATCH V3] " Feng Xue OS
  0 siblings, 1 reply; 28+ messages in thread
From: Feng Xue OS @ 2019-09-06 16:39 UTC (permalink / raw)
  To: Martin Jambor, Jan Hubicka, gcc-patches

Current IPA only supports a simple kind of CP on by-ref argument, that is
directly defined with a constant value before callsite. This patch is meant
to extend CP to handle a more generalized form on by-ref argument
definition, which is similar to what have done on by-value argument. It will
cover the following patterns. ("arg" stands for callee actual argument, and
param is caller formal parameter.)

    *arg = unary-op  param
    *arg = unary-op *param
    *arg =  param binary-op constant
    *arg = *param binary-op constant

Feng
---
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 33d52fe5537..b0ff48d130e 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1244,23 +1244,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)
@@ -1274,8 +1274,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;
@@ -1283,6 +1282,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.  */
 
@@ -1416,6 +1430,150 @@ 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 load size is same as source value size.  */
+      if (TYPE_SIZE (load_type) != TYPE_SIZE (value_type))
+	return NULL_TREE;
+
+      /* 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.  */
@@ -1592,16 +1750,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;
@@ -1611,17 +1778,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 ();
       }
@@ -1629,6 +1801,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.  */
@@ -1789,7 +1979,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
 	      added_sth = true;
 	    }
 	}
-
     }
 
  prop_fail:
@@ -2145,6 +2334,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.  */
 
@@ -2212,15 +2480,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)
@@ -2326,7 +2593,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)
 {
@@ -2364,9 +2631,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;
@@ -2420,8 +2687,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);
     }
@@ -2543,7 +2809,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,
@@ -2557,7 +2823,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;
@@ -2691,25 +2957,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;
 }
@@ -2725,7 +2991,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);
@@ -2775,40 +3041,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
@@ -2818,7 +3064,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)
 {
@@ -2827,7 +3073,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)
@@ -2841,7 +3087,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;
 
@@ -2867,8 +3113,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;
 
@@ -2881,9 +3126,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->local.can_change_signature))
     {
@@ -2896,7 +3140,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);
@@ -2959,7 +3203,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))
@@ -2994,7 +3238,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))
@@ -3013,13 +3257,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;
@@ -3031,14 +3275,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))
@@ -3054,18 +3298,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);
 }
 
 
@@ -3433,26 +3673,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
@@ -4074,10 +4294,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;
@@ -4085,7 +4305,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);
@@ -4098,11 +4318,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)
@@ -4140,18 +4360,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;
@@ -4167,11 +4387,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);
@@ -4208,9 +4428,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);
@@ -4291,12 +4511,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)
 	  {
@@ -4314,9 +4548,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;
 		  }
@@ -4329,7 +4564,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
   else
     {
       inter.release ();
-      return vec<ipa_agg_jf_item>();
+      return vNULL;
     }
   return inter;
 }
@@ -4357,8 +4592,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;
 
@@ -4465,7 +4700,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)
@@ -4488,7 +4723,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)
@@ -4726,7 +4961,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)
@@ -4737,8 +4971,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++)
     {
@@ -4807,9 +5040,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 278bf606661..413ba219bbf 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -306,9 +306,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.  */
 
@@ -316,8 +316,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)
 {
@@ -347,7 +346,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
@@ -356,7 +355,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);
 	    }
@@ -420,12 +419,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;
 
   if (clause_ptr)
     *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
@@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
       && !e->call_stmt_cannot_inline_p
       && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
     {
+      struct cgraph_node *caller;
       class ipa_node_params *caller_parms_info, *callee_pi;
       class ipa_edge_args *args = IPA_EDGE_REF (e);
       class ipa_call_summary *es = ipa_call_summaries->get (e);
       int i, count = ipa_get_cs_argument_count (args);
 
       if (e->caller->global.inlined_to)
-	caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
+	caller = e->caller->global.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))
@@ -481,10 +482,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
@@ -516,7 +516,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);
 }
 
 
@@ -2499,7 +2499,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;
@@ -2548,7 +2548,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);
@@ -2583,7 +2583,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)
@@ -2646,7 +2646,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
 			     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,
 			     int *ret_size, int *ret_min_size,
 			     sreal *ret_time,
 			     sreal *ret_nonspecialized_time,
@@ -2782,7 +2782,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 173d3f2a652..7e561dab400 100644
--- a/gcc/ipa-fnsummary.h
+++ b/gcc/ipa-fnsummary.h
@@ -260,7 +260,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);
@@ -274,13 +274,13 @@ void 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> *);
+				   vec<ipa_agg_value_set> *);
 void estimate_node_size_and_time (struct cgraph_node *node,
 				  clause_t possible_truths,
 				  clause_t nonspec_possible_truths,
 				  vec<tree> known_vals,
 				  vec<ipa_polymorphic_call_context>,
-				  vec<ipa_agg_jump_function_p> known_aggs,
+				  vec<ipa_agg_value_set> known_aggs,
 				  int *ret_size, int *ret_min_size,
 				  sreal *ret_time,
 				  sreal *ret_nonspecialized_time,
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index a66af277d03..bf4e6ea3392 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -127,7 +127,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;
 
@@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
 
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   gcc_checking_assert (size >= 0);
   gcc_checking_assert (time >= 0);
 
@@ -186,7 +186,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.  */
 
@@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
 			       NULL, NULL, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   return size;
 }
 
@@ -227,7 +227,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.  */
 
@@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
 			       NULL, NULL, &hints, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   hints |= simple_edge_hints (edge);
   return hints;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index a23aa2590a0..14f3abf8fa7 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -359,18 +359,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");
 	    }
 	}
@@ -870,7 +897,7 @@ find_dominating_aa_status (struct ipa_func_body_info *fbi, basic_block bb,
 }
 
 /* Get AA status structure for the given BB and parameter with INDEX.  Allocate
-   structures and/or intialize the result with a dominating description as
+   structures and/or initialize the result with a dominating description as
    necessary.  */
 
 static struct ipa_param_aa_status *
@@ -1135,6 +1162,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 involving 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;
+
+  if (!ipa_load_from_parm_agg (fbi, info->descriptors, stmt, rhs, &index,
+			       offset_p, &size, by_ref_p))
+    return -1;
+
+  /* Skip bit-fields.  */
+  if (TREE_CODE (rhs) == BIT_FIELD_REF
+      || contains_bitfld_component_ref_p (rhs))
+    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
@@ -1438,11 +1526,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))
     {
@@ -1452,25 +1540,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,
@@ -1490,7 +1584,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
@@ -1510,27 +1604,167 @@ 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)
     {
-      if (list->constant)
+      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;
+      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 disginguish 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;
+
+  /* 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))
+    {
+    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:
+      if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs1)))
+	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
@@ -1540,26 +1774,20 @@ 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_type = TREE_TYPE (lhs);
+  tree lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset, &lhs_size,
+					       &reverse);
   if (!lhs_base)
     return false;
 
@@ -1573,32 +1801,44 @@ 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 = lhs_type;
   content->next = NULL;
 
+  /* Initialize jump function data for the aggregate part.  */
+  memset (&content->value, 0, sizeof (content->value));
+  content->value.pass_through.operation = NOP_EXPR;
+  content->value.pass_through.formal_id = -1;
+  content->value.offset = -1;
+
+  if (AGGREGATE_TYPE_P (lhs_type)
+      || TREE_THIS_VOLATILE (lhs)
+      || TREE_CODE (lhs) == BIT_FIELD_REF
+      || contains_bitfld_component_ref_p (lhs))
+    return true;
+
+  gcc_assert (tree_to_shwi (TYPE_SIZE (lhs_type)) == lhs_size);
+  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;
@@ -1677,7 +1917,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;
 	}
@@ -1687,12 +1927,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
@@ -1702,7 +1943,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;
 	    }
 
@@ -1720,12 +1961,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);
     }
 }
 
@@ -2017,8 +2258,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);
@@ -2661,6 +2901,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;
@@ -3093,18 +3399,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)
@@ -3122,7 +3429,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
@@ -3218,11 +3525,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;
@@ -3232,10 +3541,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)
@@ -3289,12 +3602,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;
@@ -3312,9 +3629,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;
@@ -3405,14 +3726,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->global.inlined_to
-				? cs->caller->global.inlined_to
-				: cs->caller);
+  new_root = cs->caller->global.inlined_to
+		? cs->caller->global.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)
@@ -3451,13 +3773,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);
 	}
 
@@ -4125,6 +4450,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);
@@ -4138,8 +4465,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_binary)
+	    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);
@@ -4236,8 +4591,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_binary)
+	    item.value.pass_through.operand = stream_read_tree (ib, data_in);
+	  else
+	    item.value.pass_through.operand = NULL_TREE;
+          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);
     }
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 30948fb8854..c31fbd47421 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 one
+   primary operation on formal parameter is memory dereference that loads
+   a value from a part of an aggregate, which is represented or pointed 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 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 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,38 +140,86 @@ 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;
+
+  /* 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). */
   bool by_ref;
 };
 
-typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
+/* 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;
+
+  /* The known constant.  */
+  tree value;
+};
+
+/* 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;
+
+  void release ()
+  {
+    items.release ();
+  }
+};
+
+/* 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
@@ -172,8 +250,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
@@ -199,7 +277,7 @@ struct GTY (()) ipa_jump_func
 };
 
 
-/* Return the constant stored in a constant jump functin JFUNC.  */
+/* Return the constant stored in a constant jump function JFUNC.  */
 
 static inline tree
 ipa_get_jf_constant (struct ipa_jump_func *jfunc)
@@ -290,7 +368,7 @@ ipa_get_jf_ancestor_agg_preserved (struct ipa_jump_func *jfunc)
   return jfunc->value.ancestor.agg_preserved;
 }
 
-/* Return true if ancestor jump function JFUNC presrves type information.  */
+/* Return true if ancestor jump function JFUNC preserves type information.  */
 
 static inline bool
 ipa_get_jf_ancestor_type_preserved (struct ipa_jump_func *jfunc)
@@ -742,9 +820,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);
@@ -757,7 +835,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,
@@ -803,6 +881,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..67e0b3a9a93
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
@@ -0,0 +1,78 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fdump-tree-optimized-slim -fno-early-inlining"  } */
+/* { dg-add-options bind_pic_locally } */
+
+struct S
+{
+  int a, b, c;
+};
+
+void *blah(int, void *);
+
+static void inline
+foo (struct S *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 (&s);
+}
+
+static void __attribute__ ((noinline))
+foo_r (struct S *p)
+{
+  foo (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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH V3] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682))
  2019-09-06 16:39 ` [PATCH V2] " Feng Xue OS
@ 2019-09-09  8:40   ` Feng Xue OS
  2019-09-19 14:30     ` [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682) Feng Xue OS
  0 siblings, 1 reply; 28+ messages in thread
From: Feng Xue OS @ 2019-09-09  8:40 UTC (permalink / raw)
  To: Martin Jambor, Jan Hubicka, gcc-patches

Made some changes.

Feng
---
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 33d52fe5537..f218f1093b8 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1244,23 +1244,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)
@@ -1274,8 +1274,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;
@@ -1283,6 +1282,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.  */
 
@@ -1416,6 +1430,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.  */
@@ -1592,16 +1746,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;
@@ -1611,17 +1774,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 ();
       }
@@ -1629,6 +1797,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.  */
@@ -1789,7 +1975,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
 	      added_sth = true;
 	    }
 	}
-
     }
 
  prop_fail:
@@ -2145,6 +2330,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.  */
 
@@ -2212,15 +2476,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)
@@ -2326,7 +2589,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)
 {
@@ -2364,9 +2627,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;
@@ -2420,8 +2683,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);
     }
@@ -2543,7 +2805,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,
@@ -2557,7 +2819,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;
@@ -2691,25 +2953,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;
 }
@@ -2725,7 +2987,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);
@@ -2775,40 +3037,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
@@ -2818,7 +3060,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)
 {
@@ -2827,7 +3069,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)
@@ -2841,7 +3083,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;
 
@@ -2867,8 +3109,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;
 
@@ -2881,9 +3122,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->local.can_change_signature))
     {
@@ -2896,7 +3136,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);
@@ -2959,7 +3199,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))
@@ -2994,7 +3234,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))
@@ -3013,13 +3253,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;
@@ -3031,14 +3271,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))
@@ -3054,18 +3294,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);
 }
 
 
@@ -3433,26 +3669,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
@@ -4074,10 +4290,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;
@@ -4085,7 +4301,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);
@@ -4098,11 +4314,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)
@@ -4140,18 +4356,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;
@@ -4167,11 +4383,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);
@@ -4208,9 +4424,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);
@@ -4291,12 +4507,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)
 	  {
@@ -4314,9 +4544,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;
 		  }
@@ -4329,7 +4560,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
   else
     {
       inter.release ();
-      return vec<ipa_agg_jf_item>();
+      return vNULL;
     }
   return inter;
 }
@@ -4357,8 +4588,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;
 
@@ -4465,7 +4696,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)
@@ -4488,7 +4719,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)
@@ -4726,7 +4957,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)
@@ -4737,8 +4967,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++)
     {
@@ -4807,9 +5036,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 278bf606661..df1fc479884 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -306,9 +306,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.  */
 
@@ -316,8 +316,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)
 {
@@ -347,7 +346,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
@@ -356,7 +355,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);
 	    }
@@ -420,12 +419,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;
 
   if (clause_ptr)
     *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
@@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
       && !e->call_stmt_cannot_inline_p
       && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
     {
+      struct cgraph_node *caller;
       class ipa_node_params *caller_parms_info, *callee_pi;
       class ipa_edge_args *args = IPA_EDGE_REF (e);
       class ipa_call_summary *es = ipa_call_summaries->get (e);
       int i, count = ipa_get_cs_argument_count (args);
 
       if (e->caller->global.inlined_to)
-	caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
+	caller = e->caller->global.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))
@@ -481,10 +482,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
@@ -516,7 +516,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);
 }
 
 
@@ -2499,7 +2499,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;
@@ -2548,7 +2548,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);
@@ -2583,7 +2583,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)
@@ -2646,7 +2646,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
 			     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,
 			     int *ret_size, int *ret_min_size,
 			     sreal *ret_time,
 			     sreal *ret_nonspecialized_time,
@@ -2782,7 +2782,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 173d3f2a652..7e561dab400 100644
--- a/gcc/ipa-fnsummary.h
+++ b/gcc/ipa-fnsummary.h
@@ -260,7 +260,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);
@@ -274,13 +274,13 @@ void 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> *);
+				   vec<ipa_agg_value_set> *);
 void estimate_node_size_and_time (struct cgraph_node *node,
 				  clause_t possible_truths,
 				  clause_t nonspec_possible_truths,
 				  vec<tree> known_vals,
 				  vec<ipa_polymorphic_call_context>,
-				  vec<ipa_agg_jump_function_p> known_aggs,
+				  vec<ipa_agg_value_set> known_aggs,
 				  int *ret_size, int *ret_min_size,
 				  sreal *ret_time,
 				  sreal *ret_nonspecialized_time,
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index a66af277d03..bf4e6ea3392 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -127,7 +127,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;
 
@@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
 
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   gcc_checking_assert (size >= 0);
   gcc_checking_assert (time >= 0);
 
@@ -186,7 +186,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.  */
 
@@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
 			       NULL, NULL, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   return size;
 }
 
@@ -227,7 +227,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.  */
 
@@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
 			       NULL, NULL, &hints, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   hints |= simple_edge_hints (edge);
   return hints;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index a23aa2590a0..2fd8b3ebc79 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -359,18 +359,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");
 	    }
 	}
@@ -870,7 +897,7 @@ find_dominating_aa_status (struct ipa_func_body_info *fbi, basic_block bb,
 }
 
 /* Get AA status structure for the given BB and parameter with INDEX.  Allocate
-   structures and/or intialize the result with a dominating description as
+   structures and/or initialize the result with a dominating description as
    necessary.  */
 
 static struct ipa_param_aa_status *
@@ -1135,6 +1162,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
@@ -1438,11 +1526,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))
     {
@@ -1452,25 +1540,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,
@@ -1490,7 +1584,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
@@ -1510,27 +1604,181 @@ 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 disginguish 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:
+      if (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
@@ -1540,26 +1788,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;
 
@@ -1573,32 +1814,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;
@@ -1677,7 +1917,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;
 	}
@@ -1687,12 +1927,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
@@ -1702,7 +1943,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;
 	    }
 
@@ -1720,12 +1961,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);
     }
 }
 
@@ -2017,8 +2258,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);
@@ -2661,6 +2901,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;
@@ -2700,8 +3006,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;
@@ -3093,18 +3402,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)
@@ -3122,7 +3432,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
@@ -3218,11 +3528,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;
@@ -3232,10 +3544,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)
@@ -3289,12 +3605,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;
@@ -3312,9 +3632,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;
@@ -3405,14 +3729,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->global.inlined_to
-				? cs->caller->global.inlined_to
-				: cs->caller);
+  new_root = cs->caller->global.inlined_to
+		? cs->caller->global.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)
@@ -3451,13 +3776,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);
 	}
 
@@ -4125,6 +4453,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);
@@ -4138,8 +4468,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_binary)
+	    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);
@@ -4236,8 +4594,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_binary)
+	    item.value.pass_through.operand = stream_read_tree (ib, data_in);
+	  else
+	    item.value.pass_through.operand = NULL_TREE;
+	  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);
     }
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 30948fb8854..59090b3f938 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,38 +140,86 @@ 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;
 
+  /* Jump function type.  */
+  enum jump_func_type jftype;
 
-/* Aggregate jump function - i.e. description of contents of aggregates passed
-   either by reference or value.  */
+  /* 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;
+};
+
+/* 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;
+
+  /* The known constant.  */
+  tree value;
+};
+
+/* 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;
+
+  void release ()
+  {
+    items.release ();
+  }
 };
 
-typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
+/* 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
@@ -172,8 +250,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
@@ -199,7 +277,7 @@ struct GTY (()) ipa_jump_func
 };
 
 
-/* Return the constant stored in a constant jump functin JFUNC.  */
+/* Return the constant stored in a constant jump function JFUNC.  */
 
 static inline tree
 ipa_get_jf_constant (struct ipa_jump_func *jfunc)
@@ -290,7 +368,7 @@ ipa_get_jf_ancestor_agg_preserved (struct ipa_jump_func *jfunc)
   return jfunc->value.ancestor.agg_preserved;
 }
 
-/* Return true if ancestor jump function JFUNC presrves type information.  */
+/* Return true if ancestor jump function JFUNC preserves type information.  */
 
 static inline bool
 ipa_get_jf_ancestor_type_preserved (struct ipa_jump_func *jfunc)
@@ -742,9 +820,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);
@@ -757,7 +835,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,
@@ -803,6 +881,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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-09-09  8:40   ` [PATCH V3] " Feng Xue OS
@ 2019-09-19 14:30     ` Feng Xue OS
  2019-09-30  8:53       ` Ping: " Feng Xue OS
  2019-10-15 16:12       ` [PATCH V4] " Philipp Tomsich
  0 siblings, 2 replies; 28+ messages in thread
From: Feng Xue OS @ 2019-09-19 14:30 UTC (permalink / raw)
  To: Martin Jambor, Jan Hubicka, gcc-patches

Fix a bug on unary/binary operation check.

Feng
---
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 33d52fe5537..f218f1093b8 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1244,23 +1244,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)
@@ -1274,8 +1274,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;
@@ -1283,6 +1282,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.  */
 
@@ -1416,6 +1430,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.  */
@@ -1592,16 +1746,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;
@@ -1611,17 +1774,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 ();
       }
@@ -1629,6 +1797,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.  */
@@ -1789,7 +1975,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
 	      added_sth = true;
 	    }
 	}
-
     }
 
  prop_fail:
@@ -2145,6 +2330,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.  */
 
@@ -2212,15 +2476,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)
@@ -2326,7 +2589,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)
 {
@@ -2364,9 +2627,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;
@@ -2420,8 +2683,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);
     }
@@ -2543,7 +2805,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,
@@ -2557,7 +2819,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;
@@ -2691,25 +2953,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;
 }
@@ -2725,7 +2987,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);
@@ -2775,40 +3037,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
@@ -2818,7 +3060,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)
 {
@@ -2827,7 +3069,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)
@@ -2841,7 +3083,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;
 
@@ -2867,8 +3109,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;
 
@@ -2881,9 +3122,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->local.can_change_signature))
     {
@@ -2896,7 +3136,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);
@@ -2959,7 +3199,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))
@@ -2994,7 +3234,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))
@@ -3013,13 +3253,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;
@@ -3031,14 +3271,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))
@@ -3054,18 +3294,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);
 }
 
 
@@ -3433,26 +3669,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
@@ -4074,10 +4290,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;
@@ -4085,7 +4301,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);
@@ -4098,11 +4314,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)
@@ -4140,18 +4356,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;
@@ -4167,11 +4383,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);
@@ -4208,9 +4424,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);
@@ -4291,12 +4507,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)
 	  {
@@ -4314,9 +4544,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;
 		  }
@@ -4329,7 +4560,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
   else
     {
       inter.release ();
-      return vec<ipa_agg_jf_item>();
+      return vNULL;
     }
   return inter;
 }
@@ -4357,8 +4588,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;
 
@@ -4465,7 +4696,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)
@@ -4488,7 +4719,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)
@@ -4726,7 +4957,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)
@@ -4737,8 +4967,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++)
     {
@@ -4807,9 +5036,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 6de060aa3fc..f8725d8dbfe 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -306,9 +306,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.  */
 
@@ -316,8 +316,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)
 {
@@ -347,7 +346,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
@@ -356,7 +355,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);
 	    }
@@ -420,12 +419,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;
 
   if (clause_ptr)
     *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
@@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
       && !e->call_stmt_cannot_inline_p
       && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
     {
+      struct cgraph_node *caller;
       class ipa_node_params *caller_parms_info, *callee_pi;
       class ipa_edge_args *args = IPA_EDGE_REF (e);
       class ipa_call_summary *es = ipa_call_summaries->get (e);
       int i, count = ipa_get_cs_argument_count (args);
 
       if (e->caller->global.inlined_to)
-	caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
+	caller = e->caller->global.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))
@@ -481,10 +482,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
@@ -516,7 +516,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);
 }
 
 
@@ -2662,7 +2662,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;
@@ -2711,7 +2711,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);
@@ -2746,7 +2746,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)
@@ -2809,7 +2809,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
 			     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,
 			     int *ret_size, int *ret_min_size,
 			     sreal *ret_time,
 			     sreal *ret_nonspecialized_time,
@@ -2945,7 +2945,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 173d3f2a652..7e561dab400 100644
--- a/gcc/ipa-fnsummary.h
+++ b/gcc/ipa-fnsummary.h
@@ -260,7 +260,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);
@@ -274,13 +274,13 @@ void 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> *);
+				   vec<ipa_agg_value_set> *);
 void estimate_node_size_and_time (struct cgraph_node *node,
 				  clause_t possible_truths,
 				  clause_t nonspec_possible_truths,
 				  vec<tree> known_vals,
 				  vec<ipa_polymorphic_call_context>,
-				  vec<ipa_agg_jump_function_p> known_aggs,
+				  vec<ipa_agg_value_set> known_aggs,
 				  int *ret_size, int *ret_min_size,
 				  sreal *ret_time,
 				  sreal *ret_nonspecialized_time,
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index a66af277d03..bf4e6ea3392 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -127,7 +127,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;
 
@@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
 
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   gcc_checking_assert (size >= 0);
   gcc_checking_assert (time >= 0);
 
@@ -186,7 +186,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.  */
 
@@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
 			       NULL, NULL, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   return size;
 }
 
@@ -227,7 +227,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.  */
 
@@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
 			       NULL, NULL, &hints, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   hints |= simple_edge_hints (edge);
   return hints;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index a23aa2590a0..be281293eb7 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -359,18 +359,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");
 	    }
 	}
@@ -1135,6 +1162,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
@@ -1438,11 +1526,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))
     {
@@ -1452,25 +1540,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,
@@ -1490,7 +1584,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
@@ -1510,27 +1604,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 disginguish 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
@@ -1540,26 +1796,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;
 
@@ -1573,32 +1822,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;
@@ -1677,7 +1925,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;
 	}
@@ -1687,12 +1935,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
@@ -1702,7 +1951,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;
 	    }
 
@@ -1720,12 +1969,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);
     }
 }
 
@@ -2017,8 +2266,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);
@@ -2661,6 +2909,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;
@@ -2700,8 +3014,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;
@@ -3093,18 +3410,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)
@@ -3122,7 +3440,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
@@ -3218,11 +3536,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;
@@ -3232,10 +3552,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)
@@ -3289,12 +3613,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;
@@ -3312,9 +3640,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;
@@ -3405,14 +3737,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->global.inlined_to
-				? cs->caller->global.inlined_to
-				: cs->caller);
+  new_root = cs->caller->global.inlined_to
+		? cs->caller->global.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)
@@ -3451,13 +3784,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);
 	}
 
@@ -4125,6 +4461,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);
@@ -4138,8 +4476,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);
@@ -4236,8 +4602,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);
     }
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 30948fb8854..bcdcc4b7f02 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,38 +140,86 @@ 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;
 
+  /* Jump function type.  */
+  enum jump_func_type jftype;
 
-/* Aggregate jump function - i.e. description of contents of aggregates passed
-   either by reference or value.  */
+  /* 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;
+};
+
+/* 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;
+
+  /* The known constant.  */
+  tree value;
+};
+
+/* 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;
+
+  void release ()
+  {
+    items.release ();
+  }
 };
 
-typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
+/* 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
@@ -172,8 +250,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
@@ -742,9 +820,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);
@@ -757,7 +835,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,
@@ -803,6 +881,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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Ping: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-09-19 14:30     ` [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682) Feng Xue OS
@ 2019-09-30  8:53       ` Feng Xue OS
  2019-10-23  8:25         ` luoxhu
  2019-10-15 16:12       ` [PATCH V4] " Philipp Tomsich
  1 sibling, 1 reply; 28+ messages in thread
From: Feng Xue OS @ 2019-09-30  8:53 UTC (permalink / raw)
  To: Martin Jambor, Jan Hubicka, gcc-patches

Hi Honza & Martin,

   And also hope your comments on this patch. Thanks.

Feng

________________________________________
From: Feng Xue OS <fxue@os.amperecomputing.com>
Sent: Thursday, September 19, 2019 10:30 PM
To: Martin Jambor; Jan Hubicka; gcc-patches@gcc.gnu.org
Subject: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)

Fix a bug on unary/binary operation check.

Feng
---
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 33d52fe5537..f218f1093b8 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1244,23 +1244,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)
@@ -1274,8 +1274,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;
@@ -1283,6 +1282,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.  */

@@ -1416,6 +1430,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.  */
@@ -1592,16 +1746,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;
@@ -1611,17 +1774,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 ();
       }
@@ -1629,6 +1797,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.  */
@@ -1789,7 +1975,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
              added_sth = true;
            }
        }
-
     }

  prop_fail:
@@ -2145,6 +2330,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.  */

@@ -2212,15 +2476,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)
@@ -2326,7 +2589,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)
 {
@@ -2364,9 +2627,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;
@@ -2420,8 +2683,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);
     }
@@ -2543,7 +2805,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,
@@ -2557,7 +2819,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;
@@ -2691,25 +2953,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;
 }
@@ -2725,7 +2987,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);
@@ -2775,40 +3037,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
@@ -2818,7 +3060,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)
 {
@@ -2827,7 +3069,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)
@@ -2841,7 +3083,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;

@@ -2867,8 +3109,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;

@@ -2881,9 +3122,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->local.can_change_signature))
     {
@@ -2896,7 +3136,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);
@@ -2959,7 +3199,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))
@@ -2994,7 +3234,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))
@@ -3013,13 +3253,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;
@@ -3031,14 +3271,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))
@@ -3054,18 +3294,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);
 }


@@ -3433,26 +3669,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
@@ -4074,10 +4290,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;
@@ -4085,7 +4301,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);
@@ -4098,11 +4314,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)
@@ -4140,18 +4356,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;
@@ -4167,11 +4383,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);
@@ -4208,9 +4424,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);
@@ -4291,12 +4507,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)
          {
@@ -4314,9 +4544,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;
                  }
@@ -4329,7 +4560,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
   else
     {
       inter.release ();
-      return vec<ipa_agg_jf_item>();
+      return vNULL;
     }
   return inter;
 }
@@ -4357,8 +4588,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;

@@ -4465,7 +4696,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)
@@ -4488,7 +4719,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)
@@ -4726,7 +4957,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)
@@ -4737,8 +4967,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++)
     {
@@ -4807,9 +5036,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 6de060aa3fc..f8725d8dbfe 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -306,9 +306,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.  */

@@ -316,8 +316,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)
 {
@@ -347,7 +346,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
@@ -356,7 +355,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);
            }
@@ -420,12 +419,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;

   if (clause_ptr)
     *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
@@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
       && !e->call_stmt_cannot_inline_p
       && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
     {
+      struct cgraph_node *caller;
       class ipa_node_params *caller_parms_info, *callee_pi;
       class ipa_edge_args *args = IPA_EDGE_REF (e);
       class ipa_call_summary *es = ipa_call_summaries->get (e);
       int i, count = ipa_get_cs_argument_count (args);

       if (e->caller->global.inlined_to)
-       caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
+       caller = e->caller->global.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))
@@ -481,10 +482,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
@@ -516,7 +516,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);
 }


@@ -2662,7 +2662,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;
@@ -2711,7 +2711,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);
@@ -2746,7 +2746,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)
@@ -2809,7 +2809,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
                             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,
                             int *ret_size, int *ret_min_size,
                             sreal *ret_time,
                             sreal *ret_nonspecialized_time,
@@ -2945,7 +2945,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 173d3f2a652..7e561dab400 100644
--- a/gcc/ipa-fnsummary.h
+++ b/gcc/ipa-fnsummary.h
@@ -260,7 +260,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);
@@ -274,13 +274,13 @@ void 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> *);
+                                  vec<ipa_agg_value_set> *);
 void estimate_node_size_and_time (struct cgraph_node *node,
                                  clause_t possible_truths,
                                  clause_t nonspec_possible_truths,
                                  vec<tree> known_vals,
                                  vec<ipa_polymorphic_call_context>,
-                                 vec<ipa_agg_jump_function_p> known_aggs,
+                                 vec<ipa_agg_value_set> known_aggs,
                                  int *ret_size, int *ret_min_size,
                                  sreal *ret_time,
                                  sreal *ret_nonspecialized_time,
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index a66af277d03..bf4e6ea3392 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -127,7 +127,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;

@@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)

   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   gcc_checking_assert (size >= 0);
   gcc_checking_assert (time >= 0);

@@ -186,7 +186,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.  */

@@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
                               NULL, NULL, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   return size;
 }

@@ -227,7 +227,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.  */

@@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
                               NULL, NULL, &hints, vNULL);
   known_vals.release ();
   known_contexts.release ();
-  known_aggs.release ();
+  ipa_release_agg_values (known_aggs);
   hints |= simple_edge_hints (edge);
   return hints;
 }
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index a23aa2590a0..be281293eb7 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -359,18 +359,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");
            }
        }
@@ -1135,6 +1162,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
@@ -1438,11 +1526,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))
     {
@@ -1452,25 +1540,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,
@@ -1490,7 +1584,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
@@ -1510,27 +1604,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 disginguish 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
@@ -1540,26 +1796,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;

@@ -1573,32 +1822,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;
@@ -1677,7 +1925,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;
        }
@@ -1687,12 +1935,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
@@ -1702,7 +1951,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;
            }

@@ -1720,12 +1969,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);
     }
 }

@@ -2017,8 +2266,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);
@@ -2661,6 +2909,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;
@@ -2700,8 +3014,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;
@@ -3093,18 +3410,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)
@@ -3122,7 +3440,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
@@ -3218,11 +3536,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;
@@ -3232,10 +3552,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)
@@ -3289,12 +3613,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;
@@ -3312,9 +3640,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;
@@ -3405,14 +3737,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->global.inlined_to
-                               ? cs->caller->global.inlined_to
-                               : cs->caller);
+  new_root = cs->caller->global.inlined_to
+               ? cs->caller->global.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)
@@ -3451,13 +3784,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);
        }

@@ -4125,6 +4461,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);
@@ -4138,8 +4476,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);
@@ -4236,8 +4602,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);
     }
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 30948fb8854..bcdcc4b7f02 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,38 +140,86 @@ 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;

+  /* Jump function type.  */
+  enum jump_func_type jftype;

-/* Aggregate jump function - i.e. description of contents of aggregates passed
-   either by reference or value.  */
+  /* 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;
+};
+
+/* 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;
+
+  /* The known constant.  */
+  tree value;
+};
+
+/* 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;
+
+  void release ()
+  {
+    items.release ();
+  }
 };

-typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
+/* 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
@@ -172,8 +250,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
@@ -742,9 +820,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);
@@ -757,7 +835,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,
@@ -803,6 +881,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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-09-19 14:30     ` [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682) Feng Xue OS
  2019-09-30  8:53       ` Ping: " Feng Xue OS
@ 2019-10-15 16:12       ` Philipp Tomsich
  2019-10-16  8:24         ` Feng Xue OS
  1 sibling, 1 reply; 28+ messages in thread
From: Philipp Tomsich @ 2019-10-15 16:12 UTC (permalink / raw)
  To: Feng Xue OS
  Cc: Martin Jambor, Jan Hubicka, gcc-patches, Christoph Müllner,
	erick.ochoa

Feng,

this now looks fine to me: what is the current schedule to get this merged?

Thanks,
Philipp.

> On 19.09.2019, at 16:30, Feng Xue OS <fxue at os dot amperecomputing dot com> wrote:
> 
> Fix a bug on unary/binary operation check.
> 
> Feng
> ---
> diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
> index 33d52fe5537..f218f1093b8 100644
> --- a/gcc/ipa-cp.c
> +++ b/gcc/ipa-cp.c
> @@ -1244,23 +1244,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)
> @@ -1274,8 +1274,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;
> @@ -1283,6 +1282,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.  */
> 
> @@ -1416,6 +1430,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.  */
> @@ -1592,16 +1746,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;
> @@ -1611,17 +1774,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 ();
>       }
> @@ -1629,6 +1797,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.  */
> @@ -1789,7 +1975,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
> 	      added_sth = true;
> 	    }
> 	}
> -
>     }
> 
>  prop_fail:
> @@ -2145,6 +2330,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.  */
> 
> @@ -2212,15 +2476,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)
> @@ -2326,7 +2589,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)
> {
> @@ -2364,9 +2627,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;
> @@ -2420,8 +2683,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);
>     }
> @@ -2543,7 +2805,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,
> @@ -2557,7 +2819,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;
> @@ -2691,25 +2953,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;
> }
> @@ -2725,7 +2987,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);
> @@ -2775,40 +3037,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
> @@ -2818,7 +3060,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)
> {
> @@ -2827,7 +3069,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)
> @@ -2841,7 +3083,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;
> 
> @@ -2867,8 +3109,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;
> 
> @@ -2881,9 +3122,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->local.can_change_signature))
>     {
> @@ -2896,7 +3136,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);
> @@ -2959,7 +3199,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))
> @@ -2994,7 +3234,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))
> @@ -3013,13 +3253,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;
> @@ -3031,14 +3271,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))
> @@ -3054,18 +3294,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);
> }
> 
> 
> @@ -3433,26 +3669,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
> @@ -4074,10 +4290,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;
> @@ -4085,7 +4301,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);
> @@ -4098,11 +4314,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)
> @@ -4140,18 +4356,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;
> @@ -4167,11 +4383,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);
> @@ -4208,9 +4424,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);
> @@ -4291,12 +4507,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)
> 	  {
> @@ -4314,9 +4544,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;
> 		  }
> @@ -4329,7 +4560,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
>   else
>     {
>       inter.release ();
> -      return vec<ipa_agg_jf_item>();
> +      return vNULL;
>     }
>   return inter;
> }
> @@ -4357,8 +4588,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;
> 
> @@ -4465,7 +4696,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)
> @@ -4488,7 +4719,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)
> @@ -4726,7 +4957,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)
> @@ -4737,8 +4967,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++)
>     {
> @@ -4807,9 +5036,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 6de060aa3fc..f8725d8dbfe 100644
> --- a/gcc/ipa-fnsummary.c
> +++ b/gcc/ipa-fnsummary.c
> @@ -306,9 +306,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.  */
> 
> @@ -316,8 +316,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)
> {
> @@ -347,7 +346,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
> @@ -356,7 +355,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);
> 	    }
> @@ -420,12 +419,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;
> 
>   if (clause_ptr)
>     *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
> @@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
>       && !e->call_stmt_cannot_inline_p
>       && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
>     {
> +      struct cgraph_node *caller;
>       class ipa_node_params *caller_parms_info, *callee_pi;
>       class ipa_edge_args *args = IPA_EDGE_REF (e);
>       class ipa_call_summary *es = ipa_call_summaries->get (e);
>       int i, count = ipa_get_cs_argument_count (args);
> 
>       if (e->caller->global.inlined_to)
> -	caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
> +	caller = e->caller->global.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))
> @@ -481,10 +482,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
> @@ -516,7 +516,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);
> }
> 
> 
> @@ -2662,7 +2662,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;
> @@ -2711,7 +2711,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);
> @@ -2746,7 +2746,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)
> @@ -2809,7 +2809,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
> 			     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,
> 			     int *ret_size, int *ret_min_size,
> 			     sreal *ret_time,
> 			     sreal *ret_nonspecialized_time,
> @@ -2945,7 +2945,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 173d3f2a652..7e561dab400 100644
> --- a/gcc/ipa-fnsummary.h
> +++ b/gcc/ipa-fnsummary.h
> @@ -260,7 +260,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);
> @@ -274,13 +274,13 @@ void 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> *);
> +				   vec<ipa_agg_value_set> *);
> void estimate_node_size_and_time (struct cgraph_node *node,
> 				  clause_t possible_truths,
> 				  clause_t nonspec_possible_truths,
> 				  vec<tree> known_vals,
> 				  vec<ipa_polymorphic_call_context>,
> -				  vec<ipa_agg_jump_function_p> known_aggs,
> +				  vec<ipa_agg_value_set> known_aggs,
> 				  int *ret_size, int *ret_min_size,
> 				  sreal *ret_time,
> 				  sreal *ret_nonspecialized_time,
> diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
> index a66af277d03..bf4e6ea3392 100644
> --- a/gcc/ipa-inline-analysis.c
> +++ b/gcc/ipa-inline-analysis.c
> @@ -127,7 +127,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;
> 
> @@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
> 
>   known_vals.release ();
>   known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>   gcc_checking_assert (size >= 0);
>   gcc_checking_assert (time >= 0);
> 
> @@ -186,7 +186,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.  */
> 
> @@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
> 			       NULL, NULL, vNULL);
>   known_vals.release ();
>   known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>   return size;
> }
> 
> @@ -227,7 +227,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.  */
> 
> @@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
> 			       NULL, NULL, &hints, vNULL);
>   known_vals.release ();
>   known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>   hints |= simple_edge_hints (edge);
>   return hints;
> }
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index a23aa2590a0..be281293eb7 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -359,18 +359,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");
> 	    }
> 	}
> @@ -1135,6 +1162,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
> @@ -1438,11 +1526,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))
>     {
> @@ -1452,25 +1540,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,
> @@ -1490,7 +1584,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
> @@ -1510,27 +1604,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 disginguish 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
> @@ -1540,26 +1796,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;
> 
> @@ -1573,32 +1822,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;
> @@ -1677,7 +1925,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;
> 	}
> @@ -1687,12 +1935,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
> @@ -1702,7 +1951,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;
> 	    }
> 
> @@ -1720,12 +1969,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);
>     }
> }
> 
> @@ -2017,8 +2266,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);
> @@ -2661,6 +2909,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;
> @@ -2700,8 +3014,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;
> @@ -3093,18 +3410,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)
> @@ -3122,7 +3440,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
> @@ -3218,11 +3536,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;
> @@ -3232,10 +3552,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)
> @@ -3289,12 +3613,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;
> @@ -3312,9 +3640,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;
> @@ -3405,14 +3737,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->global.inlined_to
> -				? cs->caller->global.inlined_to
> -				: cs->caller);
> +  new_root = cs->caller->global.inlined_to
> +		? cs->caller->global.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)
> @@ -3451,13 +3784,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);
> 	}
> 
> @@ -4125,6 +4461,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);
> @@ -4138,8 +4476,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);
> @@ -4236,8 +4602,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);
>     }
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index 30948fb8854..bcdcc4b7f02 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,38 +140,86 @@ 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;
> 
> +  /* Jump function type.  */
> +  enum jump_func_type jftype;
> 
> -/* Aggregate jump function - i.e. description of contents of aggregates passed
> -   either by reference or value.  */
> +  /* 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;
> +};
> +
> +/* 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;
> +
> +  /* The known constant.  */
> +  tree value;
> +};
> +
> +/* 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;
> +
> +  void release ()
> +  {
> +    items.release ();
> +  }
> };
> 
> -typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
> +/* 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
> @@ -172,8 +250,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
> @@ -742,9 +820,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);
> @@ -757,7 +835,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,
> @@ -803,6 +881,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
> 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-10-15 16:12       ` [PATCH V4] " Philipp Tomsich
@ 2019-10-16  8:24         ` Feng Xue OS
  0 siblings, 0 replies; 28+ messages in thread
From: Feng Xue OS @ 2019-10-16  8:24 UTC (permalink / raw)
  To: Philipp Tomsich
  Cc: Martin Jambor, Jan Hubicka, gcc-patches, Christoph Müllner,
	erick.ochoa

Hi Philipp,

  This patch is still under code review, might still need some time. Thanks,

Feng

________________________________________
From: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Sent: Wednesday, October 16, 2019 12:05 AM
To: Feng Xue OS
Cc: Martin Jambor; Jan Hubicka; gcc-patches@gcc.gnu.org; Christoph Müllner; erick.ochoa@theobroma-systems.com
Subject: Re: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)

Feng,

this now looks fine to me: what is the current schedule to get this merged?

Thanks,
Philipp.

> On 19.09.2019, at 16:30, Feng Xue OS <fxue at os dot amperecomputing dot com> wrote:
>
> Fix a bug on unary/binary operation check.
>
> Feng
> ---
> diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
> index 33d52fe5537..f218f1093b8 100644
> --- a/gcc/ipa-cp.c
> +++ b/gcc/ipa-cp.c
> @@ -1244,23 +1244,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)
> @@ -1274,8 +1274,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;
> @@ -1283,6 +1282,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.  */
>
> @@ -1416,6 +1430,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.  */
> @@ -1592,16 +1746,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;
> @@ -1611,17 +1774,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 ();
>       }
> @@ -1629,6 +1797,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.  */
> @@ -1789,7 +1975,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
>             added_sth = true;
>           }
>       }
> -
>     }
>
>  prop_fail:
> @@ -2145,6 +2330,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.  */
>
> @@ -2212,15 +2476,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)
> @@ -2326,7 +2589,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)
> {
> @@ -2364,9 +2627,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;
> @@ -2420,8 +2683,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);
>     }
> @@ -2543,7 +2805,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,
> @@ -2557,7 +2819,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;
> @@ -2691,25 +2953,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;
> }
> @@ -2725,7 +2987,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);
> @@ -2775,40 +3037,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
> @@ -2818,7 +3060,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)
> {
> @@ -2827,7 +3069,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)
> @@ -2841,7 +3083,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;
>
> @@ -2867,8 +3109,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;
>
> @@ -2881,9 +3122,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->local.can_change_signature))
>     {
> @@ -2896,7 +3136,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);
> @@ -2959,7 +3199,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))
> @@ -2994,7 +3234,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))
> @@ -3013,13 +3253,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;
> @@ -3031,14 +3271,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))
> @@ -3054,18 +3294,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);
> }
>
>
> @@ -3433,26 +3669,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
> @@ -4074,10 +4290,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;
> @@ -4085,7 +4301,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);
> @@ -4098,11 +4314,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)
> @@ -4140,18 +4356,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;
> @@ -4167,11 +4383,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);
> @@ -4208,9 +4424,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);
> @@ -4291,12 +4507,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)
>         {
> @@ -4314,9 +4544,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;
>                 }
> @@ -4329,7 +4560,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
>   else
>     {
>       inter.release ();
> -      return vec<ipa_agg_jf_item>();
> +      return vNULL;
>     }
>   return inter;
> }
> @@ -4357,8 +4588,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;
>
> @@ -4465,7 +4696,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)
> @@ -4488,7 +4719,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)
> @@ -4726,7 +4957,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)
> @@ -4737,8 +4967,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++)
>     {
> @@ -4807,9 +5036,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 6de060aa3fc..f8725d8dbfe 100644
> --- a/gcc/ipa-fnsummary.c
> +++ b/gcc/ipa-fnsummary.c
> @@ -306,9 +306,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.  */
>
> @@ -316,8 +316,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)
> {
> @@ -347,7 +346,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
> @@ -356,7 +355,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);
>           }
> @@ -420,12 +419,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;
>
>   if (clause_ptr)
>     *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
> @@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
>       && !e->call_stmt_cannot_inline_p
>       && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
>     {
> +      struct cgraph_node *caller;
>       class ipa_node_params *caller_parms_info, *callee_pi;
>       class ipa_edge_args *args = IPA_EDGE_REF (e);
>       class ipa_call_summary *es = ipa_call_summaries->get (e);
>       int i, count = ipa_get_cs_argument_count (args);
>
>       if (e->caller->global.inlined_to)
> -     caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
> +     caller = e->caller->global.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))
> @@ -481,10 +482,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
> @@ -516,7 +516,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);
> }
>
>
> @@ -2662,7 +2662,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;
> @@ -2711,7 +2711,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);
> @@ -2746,7 +2746,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)
> @@ -2809,7 +2809,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
>                            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,
>                            int *ret_size, int *ret_min_size,
>                            sreal *ret_time,
>                            sreal *ret_nonspecialized_time,
> @@ -2945,7 +2945,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 173d3f2a652..7e561dab400 100644
> --- a/gcc/ipa-fnsummary.h
> +++ b/gcc/ipa-fnsummary.h
> @@ -260,7 +260,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);
> @@ -274,13 +274,13 @@ void 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> *);
> +                                vec<ipa_agg_value_set> *);
> void estimate_node_size_and_time (struct cgraph_node *node,
>                                 clause_t possible_truths,
>                                 clause_t nonspec_possible_truths,
>                                 vec<tree> known_vals,
>                                 vec<ipa_polymorphic_call_context>,
> -                               vec<ipa_agg_jump_function_p> known_aggs,
> +                               vec<ipa_agg_value_set> known_aggs,
>                                 int *ret_size, int *ret_min_size,
>                                 sreal *ret_time,
>                                 sreal *ret_nonspecialized_time,
> diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
> index a66af277d03..bf4e6ea3392 100644
> --- a/gcc/ipa-inline-analysis.c
> +++ b/gcc/ipa-inline-analysis.c
> @@ -127,7 +127,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;
>
> @@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
>
>   known_vals.release ();
>   known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>   gcc_checking_assert (size >= 0);
>   gcc_checking_assert (time >= 0);
>
> @@ -186,7 +186,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.  */
>
> @@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
>                              NULL, NULL, vNULL);
>   known_vals.release ();
>   known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>   return size;
> }
>
> @@ -227,7 +227,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.  */
>
> @@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
>                              NULL, NULL, &hints, vNULL);
>   known_vals.release ();
>   known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>   hints |= simple_edge_hints (edge);
>   return hints;
> }
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index a23aa2590a0..be281293eb7 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -359,18 +359,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");
>           }
>       }
> @@ -1135,6 +1162,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
> @@ -1438,11 +1526,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))
>     {
> @@ -1452,25 +1540,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,
> @@ -1490,7 +1584,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
> @@ -1510,27 +1604,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 disginguish 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
> @@ -1540,26 +1796,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;
>
> @@ -1573,32 +1822,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;
> @@ -1677,7 +1925,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;
>       }
> @@ -1687,12 +1935,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
> @@ -1702,7 +1951,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;
>           }
>
> @@ -1720,12 +1969,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);
>     }
> }
>
> @@ -2017,8 +2266,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);
> @@ -2661,6 +2909,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;
> @@ -2700,8 +3014,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;
> @@ -3093,18 +3410,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)
> @@ -3122,7 +3440,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
> @@ -3218,11 +3536,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;
> @@ -3232,10 +3552,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)
> @@ -3289,12 +3613,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;
> @@ -3312,9 +3640,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;
> @@ -3405,14 +3737,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->global.inlined_to
> -                             ? cs->caller->global.inlined_to
> -                             : cs->caller);
> +  new_root = cs->caller->global.inlined_to
> +             ? cs->caller->global.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)
> @@ -3451,13 +3784,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);
>       }
>
> @@ -4125,6 +4461,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);
> @@ -4138,8 +4476,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);
> @@ -4236,8 +4602,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);
>     }
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index 30948fb8854..bcdcc4b7f02 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,38 +140,86 @@ 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;
>
> +  /* Jump function type.  */
> +  enum jump_func_type jftype;
>
> -/* Aggregate jump function - i.e. description of contents of aggregates passed
> -   either by reference or value.  */
> +  /* 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;
> +};
> +
> +/* 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;
> +
> +  /* The known constant.  */
> +  tree value;
> +};
> +
> +/* 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;
> +
> +  void release ()
> +  {
> +    items.release ();
> +  }
> };
>
> -typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
> +/* 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
> @@ -172,8 +250,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
> @@ -742,9 +820,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);
> @@ -757,7 +835,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,
> @@ -803,6 +881,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
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-09-30  8:53       ` Ping: " Feng Xue OS
@ 2019-10-23  8:25         ` luoxhu
  2019-10-23  8:29           ` Feng Xue OS
  0 siblings, 1 reply; 28+ messages in thread
From: luoxhu @ 2019-10-23  8:25 UTC (permalink / raw)
  To: Feng Xue OS, Martin Jambor, Jan Hubicka, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 97323 bytes --]


Hi Feng, 
Thanks for the patch.  It works for me as expected.
I am not a reviewer, just tiny comment after tried.
This is quite a good case for newbies to go through the ipa-cp pass.
Is it necessary to update the test case a bit as attached to include more
circumstances for callee's aggregate include both by value and by
reference when caller's aggregate is passed either by value or by reference?


Xiong Hu
Thanks


On 2019/9/30 16:53, Feng Xue OS wrote:
> Hi Honza & Martin,
> 
>     And also hope your comments on this patch. Thanks.
> 
> Feng
> 
> ________________________________________
> From: Feng Xue OS <fxue@os.amperecomputing.com>
> Sent: Thursday, September 19, 2019 10:30 PM
> To: Martin Jambor; Jan Hubicka; gcc-patches@gcc.gnu.org
> Subject: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
> 
> Fix a bug on unary/binary operation check.
> 
> Feng
> ---
> diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
> index 33d52fe5537..f218f1093b8 100644
> --- a/gcc/ipa-cp.c
> +++ b/gcc/ipa-cp.c
> @@ -1244,23 +1244,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)
> @@ -1274,8 +1274,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;
> @@ -1283,6 +1282,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.  */
> 
> @@ -1416,6 +1430,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.  */
> @@ -1592,16 +1746,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;
> @@ -1611,17 +1774,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 ();
>         }
> @@ -1629,6 +1797,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.  */
> @@ -1789,7 +1975,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
>                added_sth = true;
>              }
>          }
> -
>       }
> 
>    prop_fail:
> @@ -2145,6 +2330,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.  */
> 
> @@ -2212,15 +2476,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)
> @@ -2326,7 +2589,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)
>   {
> @@ -2364,9 +2627,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;
> @@ -2420,8 +2683,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);
>       }
> @@ -2543,7 +2805,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,
> @@ -2557,7 +2819,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;
> @@ -2691,25 +2953,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;
>   }
> @@ -2725,7 +2987,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);
> @@ -2775,40 +3037,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
> @@ -2818,7 +3060,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)
>   {
> @@ -2827,7 +3069,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)
> @@ -2841,7 +3083,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;
> 
> @@ -2867,8 +3109,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;
> 
> @@ -2881,9 +3122,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->local.can_change_signature))
>       {
> @@ -2896,7 +3136,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);
> @@ -2959,7 +3199,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))
> @@ -2994,7 +3234,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))
> @@ -3013,13 +3253,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;
> @@ -3031,14 +3271,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))
> @@ -3054,18 +3294,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);
>   }
> 
> 
> @@ -3433,26 +3669,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
> @@ -4074,10 +4290,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;
> @@ -4085,7 +4301,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);
> @@ -4098,11 +4314,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)
> @@ -4140,18 +4356,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;
> @@ -4167,11 +4383,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);
> @@ -4208,9 +4424,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);
> @@ -4291,12 +4507,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)
>            {
> @@ -4314,9 +4544,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;
>                    }
> @@ -4329,7 +4560,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
>     else
>       {
>         inter.release ();
> -      return vec<ipa_agg_jf_item>();
> +      return vNULL;
>       }
>     return inter;
>   }
> @@ -4357,8 +4588,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;
> 
> @@ -4465,7 +4696,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)
> @@ -4488,7 +4719,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)
> @@ -4726,7 +4957,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)
> @@ -4737,8 +4967,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++)
>       {
> @@ -4807,9 +5036,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 6de060aa3fc..f8725d8dbfe 100644
> --- a/gcc/ipa-fnsummary.c
> +++ b/gcc/ipa-fnsummary.c
> @@ -306,9 +306,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.  */
> 
> @@ -316,8 +316,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)
>   {
> @@ -347,7 +346,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
> @@ -356,7 +355,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);
>              }
> @@ -420,12 +419,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;
> 
>     if (clause_ptr)
>       *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
> @@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
>         && !e->call_stmt_cannot_inline_p
>         && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
>       {
> +      struct cgraph_node *caller;
>         class ipa_node_params *caller_parms_info, *callee_pi;
>         class ipa_edge_args *args = IPA_EDGE_REF (e);
>         class ipa_call_summary *es = ipa_call_summaries->get (e);
>         int i, count = ipa_get_cs_argument_count (args);
> 
>         if (e->caller->global.inlined_to)
> -       caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
> +       caller = e->caller->global.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))
> @@ -481,10 +482,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
> @@ -516,7 +516,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);
>   }
> 
> 
> @@ -2662,7 +2662,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;
> @@ -2711,7 +2711,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);
> @@ -2746,7 +2746,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)
> @@ -2809,7 +2809,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
>                               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,
>                               int *ret_size, int *ret_min_size,
>                               sreal *ret_time,
>                               sreal *ret_nonspecialized_time,
> @@ -2945,7 +2945,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 173d3f2a652..7e561dab400 100644
> --- a/gcc/ipa-fnsummary.h
> +++ b/gcc/ipa-fnsummary.h
> @@ -260,7 +260,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);
> @@ -274,13 +274,13 @@ void 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> *);
> +                                  vec<ipa_agg_value_set> *);
>   void estimate_node_size_and_time (struct cgraph_node *node,
>                                    clause_t possible_truths,
>                                    clause_t nonspec_possible_truths,
>                                    vec<tree> known_vals,
>                                    vec<ipa_polymorphic_call_context>,
> -                                 vec<ipa_agg_jump_function_p> known_aggs,
> +                                 vec<ipa_agg_value_set> known_aggs,
>                                    int *ret_size, int *ret_min_size,
>                                    sreal *ret_time,
>                                    sreal *ret_nonspecialized_time,
> diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
> index a66af277d03..bf4e6ea3392 100644
> --- a/gcc/ipa-inline-analysis.c
> +++ b/gcc/ipa-inline-analysis.c
> @@ -127,7 +127,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;
> 
> @@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
> 
>     known_vals.release ();
>     known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>     gcc_checking_assert (size >= 0);
>     gcc_checking_assert (time >= 0);
> 
> @@ -186,7 +186,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.  */
> 
> @@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
>                                 NULL, NULL, vNULL);
>     known_vals.release ();
>     known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>     return size;
>   }
> 
> @@ -227,7 +227,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.  */
> 
> @@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
>                                 NULL, NULL, &hints, vNULL);
>     known_vals.release ();
>     known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>     hints |= simple_edge_hints (edge);
>     return hints;
>   }
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index a23aa2590a0..be281293eb7 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -359,18 +359,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");
>              }
>          }
> @@ -1135,6 +1162,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
> @@ -1438,11 +1526,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))
>       {
> @@ -1452,25 +1540,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,
> @@ -1490,7 +1584,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
> @@ -1510,27 +1604,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 disginguish 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
> @@ -1540,26 +1796,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;
> 
> @@ -1573,32 +1822,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;
> @@ -1677,7 +1925,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;
>          }
> @@ -1687,12 +1935,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
> @@ -1702,7 +1951,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;
>              }
> 
> @@ -1720,12 +1969,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);
>       }
>   }
> 
> @@ -2017,8 +2266,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);
> @@ -2661,6 +2909,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;
> @@ -2700,8 +3014,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;
> @@ -3093,18 +3410,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)
> @@ -3122,7 +3440,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
> @@ -3218,11 +3536,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;
> @@ -3232,10 +3552,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)
> @@ -3289,12 +3613,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;
> @@ -3312,9 +3640,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;
> @@ -3405,14 +3737,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->global.inlined_to
> -                               ? cs->caller->global.inlined_to
> -                               : cs->caller);
> +  new_root = cs->caller->global.inlined_to
> +               ? cs->caller->global.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)
> @@ -3451,13 +3784,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);
>          }
> 
> @@ -4125,6 +4461,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);
> @@ -4138,8 +4476,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);
> @@ -4236,8 +4602,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);
>       }
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index 30948fb8854..bcdcc4b7f02 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,38 +140,86 @@ 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;
> 
> +  /* Jump function type.  */
> +  enum jump_func_type jftype;
> 
> -/* Aggregate jump function - i.e. description of contents of aggregates passed
> -   either by reference or value.  */
> +  /* 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;
> +};
> +
> +/* 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;
> +
> +  /* The known constant.  */
> +  tree value;
> +};
> +
> +/* 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;
> +
> +  void release ()
> +  {
> +    items.release ();
> +  }
>   };
> 
> -typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
> +/* 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
> @@ -172,8 +250,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
> @@ -742,9 +820,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);
> @@ -757,7 +835,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,
> @@ -803,6 +881,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
> 

[-- Attachment #2: test.diff --]
[-- Type: text/plain, Size: 919 bytes --]

diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
index 3c496eeef39..a538beda801 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
@@ -4,7 +4,7 @@

 struct S
 {
-  int a, b, c;
+  int a, b, c, d;
 };

 void *blah(int, void *);
@@ -32,22 +32,24 @@ foo_r (struct S *p)
 }

 static void
-goo_v (int a, int *p)
+goo_v (int a, int *p, struct S m)
 {
   struct S s;
   s.a = 101;
   s.b = a % 7;
-  s.c = *p + 6;
+  s.c = *p + 5;
+  s.d = m.c + 6;
   foo_v (s);
 }

 static void
-goo_r (int a, struct S n)
+goo_r (int a, struct S n, int * p)
 {
   struct S s;
   s.a = 1;
   s.b = a + 5;
-  s.c = -n.b;
+  s.c = n.c + 1;
+  s.d = *p + 2;
   foo_r (&s);
 }

@@ -60,11 +62,11 @@ entry ()

   a = 9;
   v = 3;
+  s.a = 1;
-  goo_v (a, &v);
+  goo_v (a, &v, s);

   a = 100;
   s.b = 18;
+  v = 4;
-  goo_r (a, s);
+  goo_r (a, s, &v);
 }

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-10-23  8:25         ` luoxhu
@ 2019-10-23  8:29           ` Feng Xue OS
  2019-11-04  9:39             ` Ping: [PATCH V5] " Feng Xue OS
  0 siblings, 1 reply; 28+ messages in thread
From: Feng Xue OS @ 2019-10-23  8:29 UTC (permalink / raw)
  To: luoxhu, Martin Jambor, Jan Hubicka, gcc-patches

Thanks for your comment, I will update the case accordingly.

Feng

________________________________________
From: luoxhu <luoxhu@linux.ibm.com>
Sent: Wednesday, October 23, 2019 4:02 PM
To: Feng Xue OS; Martin Jambor; Jan Hubicka; gcc-patches@gcc.gnu.org
Subject: Re: Ping: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)


Hi Feng,
Thanks for the patch.  It works for me as expected.
I am not a reviewer, just tiny comment after tried.
This is quite a good case for newbies to go through the ipa-cp pass.
Is it necessary to update the test case a bit as attached to include more
circumstances for callee's aggregate include both by value and by
reference when caller's aggregate is passed either by value or by reference?


Xiong Hu
Thanks


On 2019/9/30 16:53, Feng Xue OS wrote:
> Hi Honza & Martin,
>
>     And also hope your comments on this patch. Thanks.
>
> Feng
>
> ________________________________________
> From: Feng Xue OS <fxue@os.amperecomputing.com>
> Sent: Thursday, September 19, 2019 10:30 PM
> To: Martin Jambor; Jan Hubicka; gcc-patches@gcc.gnu.org
> Subject: [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
>
> Fix a bug on unary/binary operation check.
>
> Feng
> ---
> diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
> index 33d52fe5537..f218f1093b8 100644
> --- a/gcc/ipa-cp.c
> +++ b/gcc/ipa-cp.c
> @@ -1244,23 +1244,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)
> @@ -1274,8 +1274,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;
> @@ -1283,6 +1282,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.  */
>
> @@ -1416,6 +1430,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.  */
> @@ -1592,16 +1746,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;
> @@ -1611,17 +1774,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 ();
>         }
> @@ -1629,6 +1797,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.  */
> @@ -1789,7 +1975,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
>                added_sth = true;
>              }
>          }
> -
>       }
>
>    prop_fail:
> @@ -2145,6 +2330,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.  */
>
> @@ -2212,15 +2476,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)
> @@ -2326,7 +2589,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)
>   {
> @@ -2364,9 +2627,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;
> @@ -2420,8 +2683,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);
>       }
> @@ -2543,7 +2805,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,
> @@ -2557,7 +2819,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;
> @@ -2691,25 +2953,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;
>   }
> @@ -2725,7 +2987,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);
> @@ -2775,40 +3037,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
> @@ -2818,7 +3060,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)
>   {
> @@ -2827,7 +3069,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)
> @@ -2841,7 +3083,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;
>
> @@ -2867,8 +3109,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;
>
> @@ -2881,9 +3122,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->local.can_change_signature))
>       {
> @@ -2896,7 +3136,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);
> @@ -2959,7 +3199,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))
> @@ -2994,7 +3234,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))
> @@ -3013,13 +3253,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;
> @@ -3031,14 +3271,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))
> @@ -3054,18 +3294,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);
>   }
>
>
> @@ -3433,26 +3669,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
> @@ -4074,10 +4290,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;
> @@ -4085,7 +4301,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);
> @@ -4098,11 +4314,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)
> @@ -4140,18 +4356,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;
> @@ -4167,11 +4383,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);
> @@ -4208,9 +4424,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);
> @@ -4291,12 +4507,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)
>            {
> @@ -4314,9 +4544,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;
>                    }
> @@ -4329,7 +4560,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
>     else
>       {
>         inter.release ();
> -      return vec<ipa_agg_jf_item>();
> +      return vNULL;
>       }
>     return inter;
>   }
> @@ -4357,8 +4588,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;
>
> @@ -4465,7 +4696,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)
> @@ -4488,7 +4719,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)
> @@ -4726,7 +4957,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)
> @@ -4737,8 +4967,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++)
>       {
> @@ -4807,9 +5036,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 6de060aa3fc..f8725d8dbfe 100644
> --- a/gcc/ipa-fnsummary.c
> +++ b/gcc/ipa-fnsummary.c
> @@ -306,9 +306,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.  */
>
> @@ -316,8 +316,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)
>   {
> @@ -347,7 +346,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
> @@ -356,7 +355,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);
>              }
> @@ -420,12 +419,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;
>
>     if (clause_ptr)
>       *clause_ptr = inline_p ? 0 : 1 << predicate::not_inlined_condition;
> @@ -438,15 +437,17 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
>         && !e->call_stmt_cannot_inline_p
>         && ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
>       {
> +      struct cgraph_node *caller;
>         class ipa_node_params *caller_parms_info, *callee_pi;
>         class ipa_edge_args *args = IPA_EDGE_REF (e);
>         class ipa_call_summary *es = ipa_call_summaries->get (e);
>         int i, count = ipa_get_cs_argument_count (args);
>
>         if (e->caller->global.inlined_to)
> -       caller_parms_info = IPA_NODE_REF (e->caller->global.inlined_to);
> +       caller = e->caller->global.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))
> @@ -481,10 +482,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
> @@ -516,7 +516,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);
>   }
>
>
> @@ -2662,7 +2662,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;
> @@ -2711,7 +2711,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);
> @@ -2746,7 +2746,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)
> @@ -2809,7 +2809,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
>                               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,
>                               int *ret_size, int *ret_min_size,
>                               sreal *ret_time,
>                               sreal *ret_nonspecialized_time,
> @@ -2945,7 +2945,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 173d3f2a652..7e561dab400 100644
> --- a/gcc/ipa-fnsummary.h
> +++ b/gcc/ipa-fnsummary.h
> @@ -260,7 +260,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);
> @@ -274,13 +274,13 @@ void 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> *);
> +                                  vec<ipa_agg_value_set> *);
>   void estimate_node_size_and_time (struct cgraph_node *node,
>                                    clause_t possible_truths,
>                                    clause_t nonspec_possible_truths,
>                                    vec<tree> known_vals,
>                                    vec<ipa_polymorphic_call_context>,
> -                                 vec<ipa_agg_jump_function_p> known_aggs,
> +                                 vec<ipa_agg_value_set> known_aggs,
>                                    int *ret_size, int *ret_min_size,
>                                    sreal *ret_time,
>                                    sreal *ret_nonspecialized_time,
> diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
> index a66af277d03..bf4e6ea3392 100644
> --- a/gcc/ipa-inline-analysis.c
> +++ b/gcc/ipa-inline-analysis.c
> @@ -127,7 +127,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;
>
> @@ -154,7 +154,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
>
>     known_vals.release ();
>     known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>     gcc_checking_assert (size >= 0);
>     gcc_checking_assert (time >= 0);
>
> @@ -186,7 +186,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.  */
>
> @@ -211,7 +211,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
>                                 NULL, NULL, vNULL);
>     known_vals.release ();
>     known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>     return size;
>   }
>
> @@ -227,7 +227,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.  */
>
> @@ -252,7 +252,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
>                                 NULL, NULL, &hints, vNULL);
>     known_vals.release ();
>     known_contexts.release ();
> -  known_aggs.release ();
> +  ipa_release_agg_values (known_aggs);
>     hints |= simple_edge_hints (edge);
>     return hints;
>   }
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index a23aa2590a0..be281293eb7 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -359,18 +359,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");
>              }
>          }
> @@ -1135,6 +1162,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
> @@ -1438,11 +1526,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))
>       {
> @@ -1452,25 +1540,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,
> @@ -1490,7 +1584,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
> @@ -1510,27 +1604,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 disginguish 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
> @@ -1540,26 +1796,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;
>
> @@ -1573,32 +1822,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;
> @@ -1677,7 +1925,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;
>          }
> @@ -1687,12 +1935,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
> @@ -1702,7 +1951,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;
>              }
>
> @@ -1720,12 +1969,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);
>       }
>   }
>
> @@ -2017,8 +2266,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);
> @@ -2661,6 +2909,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;
> @@ -2700,8 +3014,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;
> @@ -3093,18 +3410,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)
> @@ -3122,7 +3440,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
> @@ -3218,11 +3536,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;
> @@ -3232,10 +3552,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)
> @@ -3289,12 +3613,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;
> @@ -3312,9 +3640,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;
> @@ -3405,14 +3737,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->global.inlined_to
> -                               ? cs->caller->global.inlined_to
> -                               : cs->caller);
> +  new_root = cs->caller->global.inlined_to
> +               ? cs->caller->global.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)
> @@ -3451,13 +3784,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);
>          }
>
> @@ -4125,6 +4461,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);
> @@ -4138,8 +4476,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);
> @@ -4236,8 +4602,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);
>       }
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index 30948fb8854..bcdcc4b7f02 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,38 +140,86 @@ 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;
>
> +  /* Jump function type.  */
> +  enum jump_func_type jftype;
>
> -/* Aggregate jump function - i.e. description of contents of aggregates passed
> -   either by reference or value.  */
> +  /* 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;
> +};
> +
> +/* 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;
> +
> +  /* The known constant.  */
> +  tree value;
> +};
> +
> +/* 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;
> +
> +  void release ()
> +  {
> +    items.release ();
> +  }
>   };
>
> -typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
> +/* 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
> @@ -172,8 +250,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
> @@ -742,9 +820,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);
> @@ -757,7 +835,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,
> @@ -803,6 +881,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
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Ping: [PATCH V5] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-10-23  8:29           ` Feng Xue OS
@ 2019-11-04  9:39             ` Feng Xue OS
  2019-11-05  1:40               ` Martin Jambor
  0 siblings, 1 reply; 28+ messages in thread
From: Feng Xue OS @ 2019-11-04  9:39 UTC (permalink / raw)
  To: Jan Hubicka, Martin Jambor, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 395 bytes --]

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


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: ipa-by-ref-cp.patch --]
[-- Type: text/x-patch; name="ipa-by-ref-cp.patch", Size: 83823 bytes --]

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


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V5] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-04  9:39             ` Ping: [PATCH V5] " Feng Xue OS
@ 2019-11-05  1:40               ` Martin Jambor
  2019-11-05  9:35                 ` Ping: [PATCH V6] " Feng Xue OS
  0 siblings, 1 reply; 28+ messages in thread
From: Martin Jambor @ 2019-11-05  1:40 UTC (permalink / raw)
  To: Feng Xue OS, Jan Hubicka; +Cc: gcc-patches

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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-05  1:40               ` Martin Jambor
@ 2019-11-05  9:35                 ` Feng Xue OS
  2019-11-12 12:34                   ` Jan Hubicka
  0 siblings, 1 reply; 28+ messages in thread
From: Feng Xue OS @ 2019-11-05  9:35 UTC (permalink / raw)
  To: Martin Jambor, Jan Hubicka; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 4738 bytes --]

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.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: ipcp-agg-jf.patch --]
[-- Type: text/x-patch; name="ipcp-agg-jf.patch", Size: 89278 bytes --]

From a6df7c7dc70f50995335425c5183f4feddf998b8 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/ChangeLog                          |  71 +++
 gcc/ipa-cp.c                           | 498 +++++++++++++++------
 gcc/ipa-fnsummary.c                    |  61 +--
 gcc/ipa-fnsummary.h                    |   8 +-
 gcc/ipa-inline-analysis.c              |   6 +-
 gcc/ipa-prop.c                         | 573 +++++++++++++++++++++----
 gcc/ipa-prop.h                         | 182 ++++++--
 gcc/testsuite/ChangeLog                |   6 +
 gcc/testsuite/gcc.dg/ipa/ipcp-agg-10.c |   8 +-
 gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c |  77 ++++
 10 files changed, 1194 insertions(+), 296 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e726ff6d0a0..4b218b32392 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,74 @@
+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.
+
 2019-11-04  Kamlesh Kumar  <kamleshbhalui@gmail.com>
 
 	* common.opt (-fabi-version): Document =14.
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 8a5f8d362f6..d4ad9d793a2 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 64c4d95ccd4..f1ee35b9ca8 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);
 }
 
 
@@ -2838,7 +2838,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;
@@ -2887,7 +2887,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);
@@ -2922,7 +2922,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)
@@ -2982,7 +2982,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),
@@ -3055,9 +3055,9 @@ ipa_call_context::duplicate_from (const ipa_call_context &ctx)
 
       for (unsigned int i = 0; i < n; i++)
 	if (ipa_is_param_used_by_indirect_call (params_summary, i)
-	    && ctx.m_known_aggs[i])
+	    && !ctx.m_known_aggs[i].is_empty ())
 	  {
-	    m_known_aggs = ctx.m_known_aggs.copy ();
+	    m_known_aggs = ipa_copy_agg_values (ctx.m_known_aggs);
 	    break;
 	  }
     }
@@ -3076,7 +3076,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 ();
 }
@@ -3176,19 +3176,22 @@ ipa_call_context::equal_to (const ipa_call_context &ctx)
 	{
 	  if (!ipa_is_param_used_by_indirect_call (params_summary, i))
 	    continue;
-	  if (i >= m_known_aggs.length () || !m_known_aggs[i])
+	  if (i >= m_known_aggs.length () || m_known_aggs[i].is_empty ())
 	    {
-	      if (i < ctx.m_known_aggs.length () && ctx.m_known_aggs[i])
+	      if (i < ctx.m_known_aggs.length ()
+		  && !ctx.m_known_aggs[i].is_empty ())
 		return false;
 	      continue;
 	    }
-	  if (i >= ctx.m_known_aggs.length () || !ctx.m_known_aggs[i])
+	  if (i >= ctx.m_known_aggs.length ()
+	      || ctx.m_known_aggs[i].is_empty ())
 	    {
-	      if (i < m_known_aggs.length () && m_known_aggs[i])
+	      if (i < m_known_aggs.length ()
+		  && !m_known_aggs[i].is_empty ())
 		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;
 	}
     }
@@ -3336,7 +3339,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 8f9eaf560ac..0d7467c01af 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 ea1fae484ff..40cb7ba3210 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;
 
@@ -295,7 +295,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.  */
 
@@ -334,7 +334,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 3a6e21bf013..0ab11f132fb 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,193 @@ 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);
+     }
+
+   Here IPA_LOAD_AGG_DATA data structure is informative enough to describe
+   constant, simple pass-through and load-vale-from-aggregate. If value
+   is constant, it will be kept in field OPERAND, and field FORMAL_ID is
+   set to -1. For simple pass-through and load-value-from-aggregate, field
+   FORMAL_ID specifies the related formal parameter index, and field
+   OFFSET can be used to distinguish them, -1 means simple pass-through,
+   otherwise means load-value-from-aggregate.  */
+
+static void
+analyze_agg_content_value (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;
+
+      stmt = SSA_NAME_DEF_STMT (rhs1);
+      if (!is_gimple_assign (stmt))
+	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 +1802,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 +1828,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;
 
+  analyze_agg_content_value (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 constants or values that are derived from caller's
+   formal parameter in the way described by some kinds of jump functions.  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 +1931,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 +1941,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 +1957,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 +1975,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 +2272,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 +2931,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 +3036,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_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 +3432,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 +3462,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 +3558,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 +3574,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 +3635,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 +3662,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 +3759,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 +3806,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);
 	}
 
@@ -4169,6 +4509,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);
@@ -4182,8 +4524,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);
@@ -4280,8 +4650,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);
     }
@@ -5323,9 +5724,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..e2c869f3d35 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 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 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.  */
+  bool is_empty () 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/ChangeLog b/gcc/testsuite/ChangeLog
index cc7aaed0158..ba0189b7dd0 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2019-11-05  Feng Xue  <fxue@os.amperecomputing.com>
+
+	PR testsuite/91682
+	* gcc.dg/ipa/ipcp-agg-10.c: Change dg-scan string.
+	* gcc.dg/ipa/ipcp-agg-11.c: New test.
+
 2019-11-04  Eric Botcazou  <ebotcazou@adacore.com>
 
 	PR testsuite/92302
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


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-05  9:35                 ` Ping: [PATCH V6] " Feng Xue OS
@ 2019-11-12 12:34                   ` Jan Hubicka
  2019-11-12 12:35                     ` Jan Hubicka
  0 siblings, 1 reply; 28+ messages in thread
From: Jan Hubicka @ 2019-11-12 12:34 UTC (permalink / raw)
  To: Feng Xue OS; +Cc: Martin Jambor, gcc-patches

> +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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-12 12:34                   ` Jan Hubicka
@ 2019-11-12 12:35                     ` Jan Hubicka
  2019-11-12 13:17                       ` Martin Jambor
                                         ` (2 more replies)
  0 siblings, 3 replies; 28+ messages in thread
From: Jan Hubicka @ 2019-11-12 12:35 UTC (permalink / raw)
  To: Feng Xue OS; +Cc: Martin Jambor, gcc-patches

> > +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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-12 12:35                     ` Jan Hubicka
@ 2019-11-12 13:17                       ` Martin Jambor
  2019-11-12 13:29                         ` Jan Hubicka
  2019-11-13 10:18                       ` Feng Xue OS
       [not found]                       ` <20191112124152.awcljza2432vnps3@kam.mff.cuni.cz>
  2 siblings, 1 reply; 28+ messages in thread
From: Martin Jambor @ 2019-11-12 13:17 UTC (permalink / raw)
  To: Jan Hubicka, Feng Xue OS; +Cc: gcc-patches

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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-12 13:17                       ` Martin Jambor
@ 2019-11-12 13:29                         ` Jan Hubicka
  2019-11-13 13:58                           ` Richard Biener
  0 siblings, 1 reply; 28+ messages in thread
From: Jan Hubicka @ 2019-11-12 13:29 UTC (permalink / raw)
  To: Martin Jambor; +Cc: Feng Xue OS, gcc-patches

> 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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-12 12:35                     ` Jan Hubicka
  2019-11-12 13:17                       ` Martin Jambor
@ 2019-11-13 10:18                       ` Feng Xue OS
  2019-11-13 14:47                         ` Martin Jambor
  2019-11-15  8:10                         ` Jan Hubicka
       [not found]                       ` <20191112124152.awcljza2432vnps3@kam.mff.cuni.cz>
  2 siblings, 2 replies; 28+ messages in thread
From: Feng Xue OS @ 2019-11-13 10:18 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Jambor, gcc-patches

Thanks. 

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

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)

> > +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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
       [not found]                       ` <20191112124152.awcljza2432vnps3@kam.mff.cuni.cz>
@ 2019-11-13 10:42                         ` Feng Xue OS
  0 siblings, 0 replies; 28+ messages in thread
From: Feng Xue OS @ 2019-11-13 10:42 UTC (permalink / raw)
  To: Jan Hubicka, Martin Jambor, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 651 bytes --]

Please check the attachment, and this patch is based on the previous extended agg-jump-function patch.

Thanks,
Feng

________________________________________
From: Jan Hubicka <hubicka@ucw.cz>
Sent: Tuesday, November 12, 2019 8:41 PM
To: Feng Xue OS
Subject: Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)

Hi,
also can you please send me links to remaining IPA patches which I need
to review? I remember there is also one on clonning self recursive
functions for exchange right?  I am sorry for taking so long on this - I
really appreachiate your work.

Honza

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: recursive-clone.patch --]
[-- Type: text/x-patch; name="recursive-clone.patch", Size: 15990 bytes --]

From e83508d8e037d5b6683a0293bd2891f482c2b1c6 Mon Sep 17 00:00:00 2001
From: Feng Xue <fxue@os.amperecomputing.com>
Date: Tue, 24 Sep 2019 11:48:26 +0800
Subject: [PATCH] cost

---
 gcc/ChangeLog                          |  18 +++
 gcc/doc/invoke.texi                    |   3 +
 gcc/ipa-cp.c                           | 210 ++++++++++++++++++++++---
 gcc/ipa-prop.h                         |   2 +
 gcc/params.opt                         |   4 +
 gcc/testsuite/ChangeLog                |   5 +
 gcc/testsuite/gcc.dg/ipa/ipa-clone-2.c |  47 ++++++
 7 files changed, 265 insertions(+), 24 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-clone-2.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 047052835f0..e800209b695 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,21 @@
+2019-11-13  Feng Xue <fxue@os.amperecomputing.com>
+
+	PR ipa/92133
+	* doc/invoke.texi (ipa-cp-max-recursion-depth): Document new option.
+	* params.opt (ipa-cp-max-recursion-depth): New.
+	* ipa-cp.c (ipcp_lattice<valtype>::add_value): Add two new parameters
+	val_pos_p and unlimited.
+	(self_recursively_generated_p): New function.
+	(get_val_across_arith_op): Likewise.
+	(propagate_vals_across_arith_jfunc): Add constant propagation for
+	self-recursive function.
+	(incorporate_penalties): Do not penalize pure self-recursive function.
+	(good_cloning_opportunity_p): Dump node_is_self_scc flag.
+	(propagate_constants_topo): Set node_is_self_scc flag for cgraph node.
+	(get_info_about_necessary_edges): Relax hotness check for edge to
+	self-recursive function.
+	* ipa-prop.h (ipa_node_params): Add new field node_is_self_scc.
+
 2019-11-13  Richard Sandiford  <richard.sandiford@arm.com>
 
 	* tree-vect-loop.c (vect_estimate_min_profitable_iters): Include
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 00eb7e77808..ff917e99ff3 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12046,6 +12046,9 @@ IPA-CP calculates its own score of cloning profitability heuristics
 and performs those cloning opportunities with scores that exceed
 @option{ipa-cp-eval-threshold}.
 
+@item ipa-cp-max-recursion-depth
+Maximum depth of recursive cloning for self-recursive function.
+
 @item ipa-cp-recursion-penalty
 Percentage penalty the recursive functions will receive when they
 are evaluated for cloning.
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 984d3b08267..420f99960fb 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -228,7 +228,9 @@ public:
   inline bool set_contains_variable ();
   bool add_value (valtype newval, cgraph_edge *cs,
 		  ipcp_value<valtype> *src_val = NULL,
-		  int src_idx = 0, HOST_WIDE_INT offset = -1);
+		  int src_idx = 0, HOST_WIDE_INT offset = -1,
+		  ipcp_value<valtype> **val_pos_p = NULL,
+		  bool unlimited = false);
   void print (FILE * f, bool dump_sources, bool dump_benefits);
 };
 
@@ -1738,22 +1740,37 @@ allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
 /* Try to add NEWVAL to LAT, potentially creating a new ipcp_value for it.  CS,
    SRC_VAL SRC_INDEX and OFFSET are meant for add_source and have the same
    meaning.  OFFSET -1 means the source is scalar and not a part of an
-   aggregate.  */
+   aggregate.  If non-NULL, VAL_POS_P specifies position in value list,
+   after which newly created ipcp_value will be inserted, and it is also
+   used to record address of the added ipcp_value before function returns.
+   UNLIMITED means whether value count should not exceed the limit given
+   by PARAM_IPA_CP_VALUE_LIST_SIZE.  */
 
 template <typename valtype>
 bool
 ipcp_lattice<valtype>::add_value (valtype newval, cgraph_edge *cs,
 				  ipcp_value<valtype> *src_val,
-				  int src_idx, HOST_WIDE_INT offset)
+				  int src_idx, HOST_WIDE_INT offset,
+				  ipcp_value<valtype> **val_pos_p,
+				  bool unlimited)
 {
   ipcp_value<valtype> *val;
 
+  if (val_pos_p)
+    {
+      for (val = values; val && val != *val_pos_p; val = val->next);
+      gcc_checking_assert (val);
+    }
+
   if (bottom)
     return false;
 
   for (val = values; val; val = val->next)
     if (values_equal_for_ipcp_p (val->value, newval))
       {
+	if (val_pos_p)
+	  *val_pos_p = val;
+
 	if (ipa_edge_within_scc (cs))
 	  {
 	    ipcp_value_source<valtype> *s;
@@ -1768,7 +1785,7 @@ ipcp_lattice<valtype>::add_value (valtype newval, cgraph_edge *cs,
 	return false;
       }
 
-  if (values_count == param_ipa_cp_value_list_size)
+  if (!unlimited && values_count == param_ipa_cp_value_list_size)
     {
       /* We can only free sources, not the values themselves, because sources
 	 of other values in this SCC might point to them.   */
@@ -1782,6 +1799,9 @@ ipcp_lattice<valtype>::add_value (valtype newval, cgraph_edge *cs,
 	    }
 	}
 
+      if (val_pos_p)
+	*val_pos_p = NULL;
+
       values = NULL;
       return set_to_bottom ();
     }
@@ -1789,11 +1809,74 @@ ipcp_lattice<valtype>::add_value (valtype newval, cgraph_edge *cs,
   values_count++;
   val = allocate_and_init_ipcp_value (newval);
   val->add_source (cs, src_val, src_idx, offset);
-  val->next = values;
-  values = val;
+  if (val_pos_p)
+    {
+      val->next = (*val_pos_p)->next;
+      (*val_pos_p)->next = val;
+      *val_pos_p = val;
+    }
+  else
+    {
+      val->next = values;
+      values = val;
+    }
+
   return true;
 }
 
+/* Return true, if a ipcp_value VAL is orginated from parameter value of
+   self-feeding recursive function by applying non-passthrough arithmetic
+   transformation.  */
+
+static bool
+self_recursively_generated_p (ipcp_value<tree> *val)
+{
+  class ipa_node_params *info = NULL;
+
+  for (ipcp_value_source<tree> *src = val->sources; src; src = src->next)
+    {
+      cgraph_edge *cs = src->cs;
+
+      if (!src->val || cs->caller != cs->callee->function_symbol ()
+	  || src->val == val)
+	return false;
+
+      if (!info)
+	info = IPA_NODE_REF (cs->caller);
+
+      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info,
+								src->index);
+      ipcp_lattice<tree> *src_lat = src->offset == -1 ? &plats->itself
+						      : plats->aggs;
+      ipcp_value<tree> *src_val;
+
+      for (src_val = src_lat->values; src_val && src_val != val;
+	   src_val = src_val->next);
+
+      if (!src_val)
+	return false;
+    }
+
+  return true;
+}
+
+static tree
+get_val_across_arith_op (enum tree_code opcode,
+			 tree opnd1_type,
+			 tree opnd2,
+			 ipcp_value<tree> *src_val,
+			 tree res_type)
+{
+  tree opnd1 = src_val->value;
+
+  /* Skip source values that is incompatible with specified type.  */
+  if (opnd1_type
+      && !useless_type_conversion_p (opnd1_type, TREE_TYPE (opnd1)))
+    return NULL_TREE;
+
+  return ipa_get_jf_arith_result (opcode, opnd1, opnd2, res_type);
+}
+
 /* 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.
@@ -1817,24 +1900,69 @@ propagate_vals_across_arith_jfunc (cgraph_edge *cs,
   ipcp_value<tree> *src_val;
   bool ret = false;
 
-  /* Do not create new values when propagating within an SCC because if there
-     are arithmetic functions with circular dependencies, there is infinite
-     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.  */
+  /* Due to circular dependencies, propagating within an SCC through arithmetic
+     transformation would create infinite number of values.  But for
+     self-feeding recursive function, we could allow propagation in a limited
+     count, and this can enable a simple kind of recursive function versioning.
+     For other scenario, we would just make lattices bottom.  */
   if (opcode != NOP_EXPR && ipa_edge_within_scc (cs))
-    ret = dest_lat->set_contains_variable ();
+    {
+      int i;
+
+      if (src_lat != dest_lat || param_ipa_cp_max_recursion_depth < 1)
+	return dest_lat->set_contains_variable ();
+
+      auto_vec<ipcp_value<tree> *, 8> val_seeds;
+
+      for (src_val = src_lat->values; src_val; src_val = src_val->next)
+	{
+	  /* Now we do not use self-recursively generated value as propagation
+	     source, this is absolutely conservative, but could avoid explosion
+	     of lattice's value space, especially when one recursive function
+	     calls another recursive.  */
+	  if (self_recursively_generated_p (src_val))
+	    {
+	      /* If the lattice has already been propagated for the call site,
+		 no need to do that again.  */
+	      for (ipcp_value_source<tree> *s = src_val->sources; s;
+		   s = s->next)
+		if (s->cs == cs)
+		  return dest_lat->set_contains_variable ();
+	    }
+	  else
+	    val_seeds.safe_push (src_val);
+	}
+
+      /* Recursively generate lattice values with a limited count.  */
+      FOR_EACH_VEC_ELT (val_seeds, i, src_val)
+	{
+	  for (int j = 1; j < param_ipa_cp_max_recursion_depth; j++)
+	    {
+	      tree cstval = get_val_across_arith_op (opcode, opnd1_type, opnd2,
+						     src_val, res_type);
+	      if (!cstval)
+		break;
+
+	      /* Try to place the new lattice value after its source, which
+		 can decrease iterations of propagate stage.  */
+	      ret |= dest_lat->add_value (cstval, cs, src_val, src_idx,
+					  src_offset, &src_val, true);
+	      gcc_checking_assert (src_val);
+	    }
+	}
+      ret |= dest_lat->set_contains_variable ();
+    }
   else
     for (src_val = src_lat->values; src_val; src_val = src_val->next)
       {
-	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);
+	/* Now we do not use self-recursively generated value as propagation
+	   source, otherwise it is easy to make value space of normal lattice
+	   overflow.  */
+	if (self_recursively_generated_p (src_val))
+	  continue;
 
+	tree cstval = get_val_across_arith_op (opcode, opnd1_type, opnd2,
+					       src_val, res_type);
 	if (cstval)
 	  ret |= dest_lat->add_value (cstval, cs, src_val, src_idx,
 				      src_offset);
@@ -2939,7 +3067,7 @@ hint_time_bonus (ipa_hints hints)
 static inline int64_t
 incorporate_penalties (ipa_node_params *info, int64_t evaluation)
 {
-  if (info->node_within_scc)
+  if (info->node_within_scc && !info->node_is_self_scc)
     evaluation = (evaluation
 		  * (100 - param_ipa_cp_recursion_penalty)) / 100;
 
@@ -2983,7 +3111,8 @@ good_cloning_opportunity_p (struct cgraph_node *node, int time_benefit,
 	  count_sum.dump (dump_file);
 	  fprintf (dump_file, "%s%s) -> evaluation: " "%" PRId64
 		 ", threshold: %i\n",
-		 info->node_within_scc ? ", scc" : "",
+		 info->node_within_scc
+		   ? (info->node_is_self_scc ? ", self_scc" : ", scc") : "",
 		 info->node_calling_single_call ? ", single_call" : "",
 		 evaluation, param_ipa_cp_eval_threshold);
 	}
@@ -3001,7 +3130,8 @@ good_cloning_opportunity_p (struct cgraph_node *node, int time_benefit,
 		 "size: %i, freq_sum: %i%s%s) -> evaluation: "
 		 "%" PRId64 ", threshold: %i\n",
 		 time_benefit, size_cost, freq_sum,
-		 info->node_within_scc ? ", scc" : "",
+		 info->node_within_scc
+		   ? (info->node_is_self_scc ? ", self_scc" : ", scc") : "",
 		 info->node_calling_single_call ? ", single_call" : "",
 		 evaluation, param_ipa_cp_eval_threshold);
 
@@ -3495,14 +3625,30 @@ propagate_constants_topo (class ipa_topo_info *topo)
       while (v)
 	{
 	  struct cgraph_edge *cs;
+	  class ipa_node_params *info = NULL;
+	  bool self_scc = true;
 
 	  for (cs = v->callees; cs; cs = cs->next_callee)
 	    if (ipa_edge_within_scc (cs))
 	      {
-		IPA_NODE_REF (v)->node_within_scc = true;
+		cgraph_node *callee = cs->callee->function_symbol ();
+
+		if (v != callee)
+		  self_scc = false;
+
+		if (!info)
+		  {
+		    info = IPA_NODE_REF (v);
+		    info->node_within_scc = true;
+		  }
+
 		if (propagate_constants_across_call (cs))
-		  push_node_to_stack (topo, cs->callee->function_symbol ());
+		  push_node_to_stack (topo, callee);
 	      }
+
+	  if (info)
+	    info->node_is_self_scc = self_scc;
+
 	  v = pop_node_from_stack (topo);
 	}
 
@@ -3884,6 +4030,9 @@ get_info_about_necessary_edges (ipcp_value<valtype> *val, cgraph_node *dest,
 	      hot |= cs->maybe_hot_p ();
 	      if (cs->caller != dest)
 		non_self_recursive = true;
+	      else if (src->val)
+		gcc_assert (values_equal_for_ipcp_p (src->val->value,
+						     val->value));
 	    }
 	  cs = get_next_cgraph_edge_clone (cs);
 	}
@@ -3897,6 +4046,19 @@ get_info_about_necessary_edges (ipcp_value<valtype> *val, cgraph_node *dest,
   *freq_sum = freq;
   *count_sum = cnt;
   *caller_count = count;
+
+  if (!hot && IPA_NODE_REF (dest)->node_within_scc)
+    {
+      struct cgraph_edge *cs;
+
+      /* Cold non-SCC source edge could trigger hot recursive execution of
+	 function. Consider the case as hot and rely on following cost model
+	 computation to further select right one.  */
+      for (cs = dest->callers; cs; cs = cs->next_caller)
+	if (cs->caller == dest && cs->maybe_hot_p ())
+	  return true;
+    }
+
   return hot;
 }
 
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 7eb96a057fe..ff7675dbf6f 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -495,6 +495,8 @@ public:
   unsigned node_dead : 1;
   /* Node is involved in a recursion, potentionally indirect.  */
   unsigned node_within_scc : 1;
+  /* Node contains only direct recursion.  */
+  unsigned node_is_self_scc : 1;
   /* Node is calling a private function called only once.  */
   unsigned node_calling_single_call : 1;
   /* False when there is something makes versioning impossible.  */
diff --git a/gcc/params.opt b/gcc/params.opt
index be0a3a15598..8951935cc77 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -210,6 +210,10 @@ Threshold ipa-cp opportunity evaluation that is still considered beneficial to c
 Common Joined UInteger Var(param_ipa_cp_loop_hint_bonus) Init(64) Param
 Compile-time bonus IPA-CP assigns to candidates which make loop bounds or strides known.
 
+-param=ipa-cp-max-recursion-depth=
+Common Joined UInteger Var(param_ipa_cp_max_recursion_depth) Init(7) Param
+Maximum depth of recursive cloning for self-recursive function.
+
 -param=ipa-cp-recursion-penalty=
 Common Joined UInteger Var(param_ipa_cp_recursion_penalty) Init(40) IntegerRange(0, 100) Param
 Percentage penalty the recursive functions will receive when they are evaluated for cloning.
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 834c17a6d7f..cd572e67fc4 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2019-11-13  Feng Xue  <fxue@os.amperecomputing.com>
+
+	PR ipa/92133
+	* gcc.dg/ipa/ipa-clone-2.c: New test.
+
 2019-11-13  Richard Sandiford  <richard.sandiford@arm.com>
 
 	* gcc.target/aarch64/sve/mask_struct_store_3.c: Add
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-clone-2.c b/gcc/testsuite/gcc.dg/ipa/ipa-clone-2.c
new file mode 100644
index 00000000000..c6c26f73ee5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-clone-2.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details -fno-early-inlining --param ipa-cp-max-recursion-depth=8" } */
+
+int fn();
+
+int data[100];
+
+int recur_fn (int i)
+{
+  int j;
+   
+  if (i == 6)
+    {
+      fn();
+      fn();
+      fn();
+      fn();
+      fn();
+      fn();
+      fn();
+      fn();
+      fn();
+      fn();
+      fn();
+      fn();
+      return 10;
+    }
+
+  data[i] = i; 
+
+  for (j = 0; j < 100; j++)
+    recur_fn (i + 1);
+
+  return i; 
+}
+
+int main ()
+{
+  int i;
+
+  for (i = 0; i < 100; i++)
+    recur_fn (1) + recur_fn (-5);
+
+  return 1;
+}
+
+/* { dg-final { scan-ipa-dump-times "Creating a specialized node of recur_fn/\[0-9\]*\\." 12 "cp" } } */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-12 13:29                         ` Jan Hubicka
@ 2019-11-13 13:58                           ` Richard Biener
  2019-11-13 14:41                             ` Jan Hubicka
  0 siblings, 1 reply; 28+ messages in thread
From: Richard Biener @ 2019-11-13 13:58 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Jambor, Feng Xue OS, gcc-patches

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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-13 13:58                           ` Richard Biener
@ 2019-11-13 14:41                             ` Jan Hubicka
  0 siblings, 0 replies; 28+ messages in thread
From: Jan Hubicka @ 2019-11-13 14:41 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Jambor, Feng Xue OS, gcc-patches

> 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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-13 10:18                       ` Feng Xue OS
@ 2019-11-13 14:47                         ` Martin Jambor
  2019-11-15  8:10                         ` Jan Hubicka
  1 sibling, 0 replies; 28+ messages in thread
From: Martin Jambor @ 2019-11-13 14:47 UTC (permalink / raw)
  To: Feng Xue OS, Jan Hubicka; +Cc: gcc-patches

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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-13 10:18                       ` Feng Xue OS
  2019-11-13 14:47                         ` Martin Jambor
@ 2019-11-15  8:10                         ` Jan Hubicka
  2019-11-15 10:31                           ` Feng Xue OS
  1 sibling, 1 reply; 28+ messages in thread
From: Jan Hubicka @ 2019-11-15  8:10 UTC (permalink / raw)
  To: Feng Xue OS; +Cc: Martin Jambor, gcc-patches

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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-15  8:10                         ` Jan Hubicka
@ 2019-11-15 10:31                           ` Feng Xue OS
  2019-11-15 14:16                             ` Jan Hubicka
  0 siblings, 1 reply; 28+ messages in thread
From: Feng Xue OS @ 2019-11-15 10:31 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Jambor, gcc-patches

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?

________________________________________
From: gcc-patches-owner@gcc.gnu.org <gcc-patches-owner@gcc.gnu.org> on behalf of Jan Hubicka <hubicka@ucw.cz>
Sent: Friday, November 15, 2019 4:09 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)

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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-15 10:31                           ` Feng Xue OS
@ 2019-11-15 14:16                             ` Jan Hubicka
  2019-11-22 12:58                               ` Tamar Christina
  0 siblings, 1 reply; 28+ messages in thread
From: Jan Hubicka @ 2019-11-15 14:16 UTC (permalink / raw)
  To: Feng Xue OS; +Cc: Martin Jambor, gcc-patches

> 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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-15 14:16                             ` Jan Hubicka
@ 2019-11-22 12:58                               ` Tamar Christina
  2019-11-22 14:48                                 ` Jan Hubicka
  0 siblings, 1 reply; 28+ messages in thread
From: Tamar Christina @ 2019-11-22 12:58 UTC (permalink / raw)
  To: Jan Hubicka, Feng Xue OS; +Cc: Martin Jambor, gcc-patches, nd

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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-22 12:58                               ` Tamar Christina
@ 2019-11-22 14:48                                 ` Jan Hubicka
  2019-11-23 14:40                                   ` Tamar Christina
  0 siblings, 1 reply; 28+ messages in thread
From: Jan Hubicka @ 2019-11-22 14:48 UTC (permalink / raw)
  To: Tamar Christina; +Cc: Feng Xue OS, Martin Jambor, gcc-patches, nd

> 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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-22 14:48                                 ` Jan Hubicka
@ 2019-11-23 14:40                                   ` Tamar Christina
  2019-11-23 15:21                                     ` Jan Hubicka
  0 siblings, 1 reply; 28+ messages in thread
From: Tamar Christina @ 2019-11-23 14:40 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Feng Xue OS, Martin Jambor, gcc-patches, nd

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

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: Ping: [PATCH V6] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)
  2019-11-23 14:40                                   ` Tamar Christina
@ 2019-11-23 15:21                                     ` Jan Hubicka
  0 siblings, 0 replies; 28+ messages in thread
From: Jan Hubicka @ 2019-11-23 15:21 UTC (permalink / raw)
  To: Tamar Christina; +Cc: Feng Xue OS, Martin Jambor, gcc-patches, nd

> 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

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2019-11-23 14:40 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-06 12:35 [PATCH] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682)) Feng Xue OS
2019-09-06 16:39 ` [PATCH V2] " Feng Xue OS
2019-09-09  8:40   ` [PATCH V3] " Feng Xue OS
2019-09-19 14:30     ` [PATCH V4] Extend IPA-CP to support arithmetically-computed value-passing on by-ref argument (PR ipa/91682) Feng Xue OS
2019-09-30  8:53       ` Ping: " Feng Xue OS
2019-10-23  8:25         ` luoxhu
2019-10-23  8:29           ` Feng Xue OS
2019-11-04  9:39             ` Ping: [PATCH V5] " Feng Xue OS
2019-11-05  1:40               ` Martin Jambor
2019-11-05  9:35                 ` Ping: [PATCH V6] " Feng Xue OS
2019-11-12 12:34                   ` Jan Hubicka
2019-11-12 12:35                     ` Jan Hubicka
2019-11-12 13:17                       ` Martin Jambor
2019-11-12 13:29                         ` Jan Hubicka
2019-11-13 13:58                           ` Richard Biener
2019-11-13 14:41                             ` Jan Hubicka
2019-11-13 10:18                       ` Feng Xue OS
2019-11-13 14:47                         ` Martin Jambor
2019-11-15  8:10                         ` Jan Hubicka
2019-11-15 10:31                           ` Feng Xue OS
2019-11-15 14:16                             ` Jan Hubicka
2019-11-22 12:58                               ` Tamar Christina
2019-11-22 14:48                                 ` Jan Hubicka
2019-11-23 14:40                                   ` Tamar Christina
2019-11-23 15:21                                     ` Jan Hubicka
     [not found]                       ` <20191112124152.awcljza2432vnps3@kam.mff.cuni.cz>
2019-11-13 10:42                         ` Feng Xue OS
2019-10-15 16:12       ` [PATCH V4] " Philipp Tomsich
2019-10-16  8:24         ` Feng Xue OS

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).