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

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

Commit Message

Feng Xue OS Sept. 19, 2019, 2:30 p.m. UTC
Fix a bug on unary/binary operation check.

Feng
---

Patch
diff mbox series

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" } } */