public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 5/5] Compute predicates for phi node results in ipa-inline-analysis.c
  2012-06-01  1:24 [PATCH 0/5] Aggregate jump functions Martin Jambor
@ 2012-06-01  1:24 ` Martin Jambor
  2012-06-01  1:24 ` [PATCH 4/5] Incorporate aggregate jump functions to inlining analysis Martin Jambor
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Martin Jambor @ 2012-06-01  1:24 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka

[-- Attachment #1: phi_result_predicates.diff --]
[-- Type: text/plain, Size: 6078 bytes --]

Hi,

this patch is basically a proof-of-concept aiming at alleviating the
following code found in Fortran functions when they look at the
contents of array descriptors:

  <bb 2>:
    stride.156_7 = strain_tensor_6(D)->dim[0].stride;
    if (stride.156_7 != 0)
      goto <bb 3>;
    else
      goto <bb 4>;

  <bb 3>:

  <bb 4>:
    # stride.156_4 = PHI <stride.156_7(3), 1(2)>

and stride.156_4 is then used for other computations.  Currently we
compute a predicate for SSA name stride.156_7 but the PHI node stops
us from having one for stride.156_4 and those computed from it.

This patch looks at phi nodes, and if its pairs of predecessors have
the same nearest common dominator, and the condition there is known to
be described by a predicate (computed either by
set_cond_stmt_execution_predicate or,
set_switch_stmt_execution_predicate, we depend on knowing how exactly
they behave), we use the parameter and offset from the predicate
condition and create one for the PHI node result, provided the
arguments of a phi node allow that, of course.

I hacked this together one evening a few days ago and I expect to talk
with Honza about how to do this properly, nevertheless the patch
passes bootstrap and testing on x86_64.

On current trunk, I need to pass -finline-limit=204 to cut down
execution time of fatigue2 polyhedron benchmark from 155 seconds to 91
seconds.  With the patch, I only need -finline-limit=166.  So there's
still some way to go but something along these lines can be part of
it.

Thanks for all comments and suggestions,

Martin


2012-05-30  Martin Jambor  <mjambor@suse.cz>

	* ipa-inline-analysis.c (known_phi_condition_for_bb): New function.
	(predicate_for_phi_result): Likewise.
	(estimate_function_body_sizes): Use the above two functions.
	(inline_analyze_function): Calculate and free dominance info.


Index: src/gcc/ipa-inline-analysis.c
===================================================================
--- src.orig/gcc/ipa-inline-analysis.c
+++ src/gcc/ipa-inline-analysis.c
@@ -1998,6 +1998,96 @@ param_change_prob (gimple stmt, int i)
   return REG_BR_PROB_BASE;
 }
 
+/* For basic block BB, find if all pairs of its predecesors have a common
+   dominator and if that dominator ends with a simple condition.  If so, return
+   TRUE and stor pointer to *C.  */
+
+static bool
+known_phi_condition_for_bb (struct inline_summary *info, basic_block bb,
+			    struct condition **c)
+{
+  edge_iterator ei;
+  edge e;
+  basic_block computed_dom = NULL;
+  basic_block prev = NULL;
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      if (prev)
+	{
+	  basic_block dom = nearest_common_dominator (CDI_DOMINATORS, prev,
+						      e->src);
+	  if (!computed_dom)
+	    computed_dom = dom;
+	  else if (dom != computed_dom)
+	    return false;
+
+	}
+      prev = e->src;
+    }
+
+  if (!computed_dom)
+    return false;
+
+  FOR_EACH_EDGE (e, ei, computed_dom->succs)
+    if (e->aux)
+      {
+	struct predicate *p;
+	int i;
+	p = (struct predicate *) e->aux;
+
+	if (p->clause[0] == 0 || p->clause[1] != 0)
+	  return false;
+	for (i = 0 ; i < NUM_CONDITIONS; i++)
+	  if (((1 << i) & p->clause[0]) == p->clause[0])
+	    break;
+	if (i == NUM_CONDITIONS || i < predicate_first_dynamic_condition)
+	  return false;
+
+	*c = VEC_index (condition, info->conds,
+			i - predicate_first_dynamic_condition);
+	return true;
+      }
+  return false;
+}
+
+/* Given a PHI statement STMT in a function described by inline properties INFO
+   and in a basic lock whose predecesors in CFG is selected according to a
+   parameter (and potentially offset) stored in condition *C, store a predicate
+   for the result of the PHI statement into NONCONSTANT_NAMES, if possible.  */
+
+static void
+predicate_for_phi_result (struct inline_summary *info, gimple stmt,
+			  struct condition *c,
+			  VEC (predicate_t, heap) *nonconstant_names)
+{
+  struct predicate p;
+  unsigned i;
+
+  for (i = 0; i < gimple_phi_num_args (stmt); i++)
+    {
+      tree arg = gimple_phi_arg (stmt, i)->def;
+      if (!is_gimple_min_invariant (arg))
+	{
+	  gcc_assert (TREE_CODE (arg) == SSA_NAME);
+	  if (true_predicate_p (VEC_index (predicate_t, nonconstant_names,
+					    SSA_NAME_VERSION (arg))))
+	      return;
+	}
+    }
+
+  p = add_condition (info, c->operand_num, c->agg_contents, c->offset,
+		     CHANGED, NULL);
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "  ");
+      print_gimple_stmt (dump_file, stmt, 0, 0);
+      fprintf (dump_file, "\t\tphi predicate: ");
+      dump_predicate (dump_file, info->conds, &p);
+    }
+  VEC_replace (predicate_t, nonconstant_names,
+	       SSA_NAME_VERSION (gimple_phi_result (stmt)), &p);
+}
 
 /* Compute function body size parameters for NODE.
    When EARLY is true, we compute only simple summaries without
@@ -2066,7 +2156,17 @@ estimate_function_body_sizes (struct cgr
 	  fprintf (dump_file, "\n BB %i predicate:", bb->index);
 	  dump_predicate (dump_file, info->conds, &bb_predicate);
 	}
-      
+
+      if (parms_info && nonconstant_names)
+	for (bsi = gsi_start_phis (bb); !gsi_end_p (bsi); gsi_next (&bsi))
+	  {
+	    struct condition *phi_condition;
+	    if (!known_phi_condition_for_bb (info, bb, &phi_condition))
+	      break;
+	    predicate_for_phi_result (info, gsi_stmt (bsi), phi_condition,
+				      nonconstant_names);
+	  }
+
       for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
 	{
 	  gimple stmt = gsi_stmt (bsi);
@@ -3109,12 +3209,16 @@ inline_analyze_function (struct cgraph_n
   push_cfun (DECL_STRUCT_FUNCTION (node->symbol.decl));
   current_function_decl = node->symbol.decl;
 
+  if (cfun)
+    calculate_dominance_info (CDI_DOMINATORS);
   if (dump_file)
     fprintf (dump_file, "\nAnalyzing function: %s/%u\n",
 	     cgraph_node_name (node), node->uid);
   if (optimize && !node->thunk.thunk_p)
     inline_indirect_intraprocedural_analysis (node);
   compute_inline_parameters (node, false);
+  if (cfun)
+    free_dominance_info (CDI_DOMINATORS);
 
   current_function_decl = NULL;
   pop_cfun ();

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

* [PATCH 0/5] Aggregate jump functions
@ 2012-06-01  1:24 Martin Jambor
  2012-06-01  1:24 ` [PATCH 5/5] Compute predicates for phi node results in ipa-inline-analysis.c Martin Jambor
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Martin Jambor @ 2012-06-01  1:24 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka

Hi,

this patch set extends the current jump functions we use in
ipa-prop.[ch], ipa-inline-analysis.c and ipa-cp.c to also carry
information about aggregates passed across function calls in a
parameter, regardless whether by value or only by reference.

The long term goal is that these description of passed aggregate
contents will be as general as possible (and reasonable) with a number
of use cases, including for example devirtualization.  However, the
initial implementation was specifically aimed at Fortran array
descriptors which represent optimization barrier which we can
currently overcome only by cranking up general inlining limits (with
all potential drawbacks that has).  Arguably I have not gone all the
way, but these patches now can inline the testcase from PR 48636
(comment #4) at -O3 and the limit increase necessary to speed up
fatigue2 polyhedron benchmark has also decreased quite a bit.

The patches making use of the aggregate jump functions so far only
improve inlining, IPA-CP bits are yet to come.

The state of the patches is somewhere in between RFC and submission
material, I certainly expect comments, suggestions and some discussion
(meanwhile I'd like to add a few more testcases and experiment a
little more on other benchmarks) but the patches are now quite stable.
Last but not least, they all pass bootstrap and testing on
x86_64-linux.

Thanks,

Martin

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

* [PATCH 4/5] Incorporate aggregate jump functions to inlining analysis
  2012-06-01  1:24 [PATCH 0/5] Aggregate jump functions Martin Jambor
  2012-06-01  1:24 ` [PATCH 5/5] Compute predicates for phi node results in ipa-inline-analysis.c Martin Jambor
@ 2012-06-01  1:24 ` Martin Jambor
  2012-06-01  1:24 ` [PATCH 2/5] Build aggregate jump functions Martin Jambor
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Martin Jambor @ 2012-06-01  1:24 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka

[-- Attachment #1: inlining_agg_analysis.diff --]
[-- Type: text/plain, Size: 34500 bytes --]

Hi,

this patch uses the aggregate jump functions created by a previous
patch to determine benefits of inlining a particular call graph edge.

It does so by fairly straightforward way.  It a flag to struct
condition to specify it is actually an aggregate value at an offset,
also newly stored in the structures.  Functions which build the
predicates specifying under which conditions CFG edges will be taken
or individual statements are actually executed, then simply also look
whether a value comes from an aggregate passed to us in a parameter
(either by value or reference) and if so, create appropriate
conditions.  Later on, predicates are evaluated as before, we only
also look at aggregate contents of the jump function of the edge we
are considering to inline when evaluating the predicates, and also
remap the offsets of the jump functions when remapping over an
ancestor jump function.

There are (at least ;-) two things which should be addressed before
this patch is committed.  First is also looking for aggregate loads
when handling built_in_constant_p built-in (clearly marked in the
patch).

Second is that we should most probably also do the type-comatibility
check we do in ipa_agg_types_propagatable_p when remaping predicates
after inlining.  Because this is inlining heuristics, this omission
currently leads only to bad estimates (which are going to be very rare
too), not miscompilations, but we should be consistent.

Otherwise, the patch, as is, passes bootstrap and testing on
x86_64-linux.  Also, this patch alone makes us inline the function bar
in testcase of PR 48636 in comment #4.

Thanks for all comments and suggestions,

Martin


2012-05-30  Martin Jambor  <mjambor@suse.cz>

	PR fortran/48636
	* ipa-inline.h (condition): New fields offset and agg_contents.

	* ipa-inline-analysis.c (add_condition): Also store agg_contents and
	offset .
	(dump_condition): Also dump aggregate conditions.
	(evaluate_conditions_for_known_args): Also handle aggregate
	conditions.  New parameter known_aggs.
	(evaluate_properties_for_edge): Gather known aggregate contents.
	(inline_node_duplication_hook): Pass NULL known_aggs to
	evaluate_conditions_for_known_args.
	(unmodified_parm): Split into unmodified_parm and unmodified_parm_1.
	(unmodified_parm_or_parm_agg_item): New function.
	(set_cond_stmt_execution_predicate): Handle values passed in
	aggregates.
	(set_switch_stmt_execution_predicate): Likewise.
	(will_be_nonconstant_predicate): Likewise.
	(estimate_edge_devirt_benefit): Pass new parameter known_aggs to
	ipa_get_indirect_edge_target.
	(estimate_calls_size_and_time): New parameter known_aggs, pass it
	recrsively to itself and to estimate_edge_devirt_benefit.
	(estimate_node_size_and_time): New vector known_aggs, pass it o
	functions which need it.
	(remap_predicate): New parameter offset_map, use it to remap aggregate
	conditions.
	(remap_edge_summaries): New parameter offset_map, pass it recursively
	to itself and to remap_predicate.
	(inline_merge_summary): Also create and populate vector offset_map.
	(do_estimate_edge_time): New vector of known aggregate contents,
	passed to functions which need it.
	(inline_read_section): Stream new fields of condition.
	(inline_write_summary): Likewise.

	* ipa-cp.c (ipa_get_indirect_edge_target): Also examine the aggregate
	contents.  Let all local callers pass NULL for known_aggs.


Index: src/gcc/ipa-inline.h
===================================================================
--- src.orig/gcc/ipa-inline.h
+++ src/gcc/ipa-inline.h
@@ -28,9 +28,11 @@ along with GCC; see the file COPYING3.
 
 typedef struct GTY(()) condition
   {
+    HOST_WIDE_INT offset;
     tree val;
     int operand_num;
     enum tree_code code;
+    bool agg_contents;
   } condition;
 
 DEF_VEC_O (condition);
Index: src/gcc/ipa-inline-analysis.c
===================================================================
--- src.orig/gcc/ipa-inline-analysis.c
+++ src/gcc/ipa-inline-analysis.c
@@ -209,17 +209,21 @@ not_inlined_predicate (void)
 
 static struct predicate
 add_condition (struct inline_summary *summary, int operand_num,
+	       bool agg_contents, HOST_WIDE_INT offset,
 	       enum tree_code code, tree val)
 {
   int i;
   struct condition *c;
   struct condition new_cond;
 
+  gcc_checking_assert (operand_num >= 0);
   for (i = 0; VEC_iterate (condition, summary->conds, i, c); i++)
     {
       if (c->operand_num == operand_num
 	  && c->code == code
-	  && c->val == val)
+	  && c->val == val
+	  && c->agg_contents == agg_contents
+	  && (!agg_contents || c->offset == offset))
         return single_cond_predicate (i + predicate_first_dynamic_condition);
     }
   /* Too many conditions.  Give up and return constant true.  */
@@ -229,6 +233,8 @@ add_condition (struct inline_summary *su
   new_cond.operand_num = operand_num;
   new_cond.code = code;
   new_cond.val = val;
+  new_cond.agg_contents = agg_contents;
+  new_cond.offset = offset;
   VEC_safe_push (condition, gc, summary->conds, &new_cond);
   return single_cond_predicate (i + predicate_first_dynamic_condition);
 }
@@ -520,6 +526,8 @@ dump_condition (FILE *f, conditions cond
       c = VEC_index (condition, conditions,
 		     cond - predicate_first_dynamic_condition);
       fprintf (f, "op%i", c->operand_num);
+      if (c->agg_contents)
+	fprintf (f, "[offset: " HOST_WIDE_INT_PRINT_DEC "]", c->offset);
       if (c->code == IS_NOT_CONSTANT)
 	{
 	  fprintf (f, " not constant");
@@ -667,8 +675,9 @@ edge_set_predicate (struct cgraph_edge *
 
 static clause_t
 evaluate_conditions_for_known_args (struct cgraph_node *node,
-				    bool inline_p,
-				    VEC (tree, heap) *known_vals)
+				bool inline_p,
+				VEC (tree, heap) *known_vals,
+				VEC (ipa_agg_jump_function_p, heap) *known_aggs)
 {
   clause_t clause = inline_p ? 0 : 1 << predicate_not_inlined_condition;
   struct inline_summary *info = inline_summary (node);
@@ -680,16 +689,43 @@ evaluate_conditions_for_known_args (stru
       tree val;
       tree res;
 
-      /* We allow call stmt to have fewer arguments than the callee
-	 function (especially for K&R style programs).  So bound
-	 check here.  */
-      if (c->operand_num < (int)VEC_length (tree, known_vals))
-        val = VEC_index (tree, known_vals, c->operand_num);
-      else
-	val = NULL;
+      /* We allow call stmt to have fewer arguments than the callee function
+	 (especially for K&R style programs).  So bound check here (we assume
+	 known_aggs vector, if non-NULL, has the same length as
+	 known_vals).  */
+      if (c->operand_num >= (int) VEC_length (tree, known_vals))
+	{
+	  clause |= 1 << (i + predicate_first_dynamic_condition);
+	  continue;
+	}
 
-      if (val == error_mark_node && c->code != CHANGED)
-	val = NULL;
+      if (c->agg_contents)
+	{
+	  struct ipa_agg_jump_function *agg;
+
+	  if (c->code == CHANGED
+	      && !POINTER_TYPE_P (TREE_TYPE (ipa_get_param (IPA_NODE_REF (node),
+							    0)))
+	      && (VEC_index (tree, known_vals, c->operand_num)
+		  == error_mark_node))
+	    continue;
+
+	  if (known_aggs)
+	    {
+	      agg = VEC_index (ipa_agg_jump_function_p, known_aggs,
+			       c->operand_num);
+	      val = ipa_find_agg_cst_for_param (agg, c->offset, node,
+						c->operand_num);
+	    }
+	  else
+	    val = NULL_TREE;
+	}
+      else
+	{
+	  val = VEC_index (tree, known_vals, c->operand_num);
+	  if (val == error_mark_node && c->code != CHANGED)
+	    val = NULL_TREE;
+	}
 
       if (!val)
 	{
@@ -712,13 +748,15 @@ evaluate_conditions_for_known_args (stru
 
 static void
 evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
-			      clause_t *clause_ptr,
-			      VEC (tree, heap) **known_vals_ptr,
-			      VEC (tree, heap) **known_binfos_ptr)
+			   clause_t *clause_ptr,
+			   VEC (tree, heap) **known_vals_ptr,
+			   VEC (tree, heap) **known_binfos_ptr,
+			   VEC (ipa_agg_jump_function_p, heap) **known_aggs_ptr)
 {
   struct cgraph_node *callee = cgraph_function_or_thunk_node (e->callee, NULL);
   struct inline_summary *info = inline_summary (callee);
   VEC (tree, heap) *known_vals = NULL;
+  VEC (ipa_agg_jump_function_p, heap) *known_aggs = NULL;
 
   if (clause_ptr)
     *clause_ptr = inline_p ? 0 : 1 << predicate_not_inlined_condition;
@@ -743,13 +781,16 @@ evaluate_properties_for_edge (struct cgr
 
       if (count && (info->conds || known_vals_ptr))
 	VEC_safe_grow_cleared (tree, heap, known_vals, count);
+      if (count && (info->conds || known_aggs_ptr))
+	VEC_safe_grow_cleared (ipa_agg_jump_function_p, heap, known_aggs,
+			       count);
       if (count && known_binfos_ptr)
 	VEC_safe_grow_cleared (tree, heap, *known_binfos_ptr, count);
 
       for (i = 0; i < count; i++)
 	{
-	  tree cst = ipa_value_from_jfunc (parms_info,
-					   ipa_get_ith_jump_func (args, i));
+	  struct ipa_jump_func *jf = ipa_get_ith_jump_func (args, i);
+	  tree cst = ipa_value_from_jfunc (parms_info, jf);
 	  if (cst)
 	    {
 	      if (known_vals && TREE_CODE (cst) != TREE_BINFO)
@@ -762,17 +803,25 @@ evaluate_properties_for_edge (struct cgr
 				  es->param,
 				  i)->change_prob)
 	    VEC_replace (tree, known_vals, i, error_mark_node);
+	  /* TODO: When IPA-CP starts merging aggregate jump functions, use its
+	     knowledge of the caller too, just like the scalar case above.  */
+	  VEC_replace (ipa_agg_jump_function_p, known_aggs, i, &jf->agg);
 	}
     }
 
   if (clause_ptr)
     *clause_ptr = evaluate_conditions_for_known_args (callee, inline_p,
-						      known_vals);
+						      known_vals, known_aggs);
 
   if (known_vals_ptr)
     *known_vals_ptr = known_vals;
   else
     VEC_free (tree, heap, known_vals);
+
+  if (known_aggs_ptr)
+    *known_aggs_ptr = known_aggs;
+  else
+    VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
 }
 
 
@@ -918,8 +967,8 @@ inline_node_duplication_hook (struct cgr
 		}
 	    }
 	}
-      possible_truths = evaluate_conditions_for_known_args (dst,
-							    false, known_vals);
+      possible_truths = evaluate_conditions_for_known_args (dst, false,
+							    known_vals, NULL);
       VEC_free (tree, heap, known_vals);
 
       account_size_time (info, 0, 0, &true_pred);
@@ -1263,11 +1312,11 @@ mark_modified (ao_ref *ao ATTRIBUTE_UNUS
   return true;
 }
 
-/* If OP reffers to value of function parameter, return 
+/* If OP refers to value of function parameter, return
    the corresponding parameter.  */
 
 static tree
-unmodified_parm (gimple stmt, tree op)
+unmodified_parm_1 (gimple stmt, tree op)
 {
   /* SSA_NAME referring to parm default def?  */
   if (TREE_CODE (op) == SSA_NAME
@@ -1286,13 +1335,109 @@ unmodified_parm (gimple stmt, tree op)
       if (!modified)
 	return op;
     }
-  /* Assignment from a parameter?  */
+  return NULL_TREE;
+}
+
+/* If OP refers to value of function parameter, return
+   the corresponding parameter.  Also traverse   */
+
+static tree
+unmodified_parm (gimple stmt, tree op)
+{
+  tree res = unmodified_parm_1 (stmt, op);
+  if (res)
+    return res;
+
   if (TREE_CODE (op) == SSA_NAME
       && !SSA_NAME_IS_DEFAULT_DEF (op)
       && gimple_assign_single_p (SSA_NAME_DEF_STMT (op)))
     return unmodified_parm (SSA_NAME_DEF_STMT (op),
 			    gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op)));
-  return NULL;
+  return NULL_TREE;
+}
+
+/* If OP refers to a value of a function parameter or value loaded from an
+   aggregate passed to a parameter (either by value or reference), return TRUE
+   and store the number of the parameter to *INDEX_P, whether it has been
+   loaded from an aggregate into *AGG_CONTENTS_P and if so, offset of the value
+   within the aggregate into *OFFSET_P.  INFO describes the function
+   parameters, STMT is the statement in which OP is used or loaded.  */
+
+static bool
+unmodified_parm_or_parm_agg_item (struct ipa_node_params *info,
+				  gimple stmt, tree op, int *index_p,
+				  bool *agg_contents_p, HOST_WIDE_INT *offset_p)
+{
+  tree base, res = unmodified_parm_1 (stmt, op);
+  HOST_WIDE_INT size, max_size;
+
+  if (res)
+    {
+      *index_p = ipa_get_param_decl_index (info, res);
+      if (*index_p < 0)
+	return false;
+      *agg_contents_p = false;
+      return true;
+    }
+
+  if (TREE_CODE (op) == SSA_NAME)
+    {
+      if (SSA_NAME_IS_DEFAULT_DEF (op)
+	  || !gimple_assign_single_p (SSA_NAME_DEF_STMT (op)))
+	return false;
+      stmt = SSA_NAME_DEF_STMT (op);
+      op = gimple_assign_rhs1 (stmt);
+      if (!handled_component_p (op)
+	  && TREE_CODE (op) == MEM_REF)
+	return unmodified_parm_or_parm_agg_item (info, stmt, op, index_p,
+						 agg_contents_p, offset_p);
+    }
+
+  base = get_ref_base_and_extent (op, offset_p, &size, &max_size);
+  if (max_size == -1 || max_size != size || *offset_p < 0)
+    return false;
+
+  if (TREE_CODE (base) == PARM_DECL)
+    {
+      bool modified = false;
+      ao_ref refd;
+
+      *index_p = ipa_get_param_decl_index (info, base);
+      if (*index_p < 0)
+	return false;
+
+      ao_ref_init (&refd, base);
+      walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified, &modified,
+			  NULL);
+      if (!modified)
+	{
+	  *agg_contents_p = true;
+	  return true;
+	}
+    }
+  else if (TREE_CODE (base) == MEM_REF
+	   && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME
+	   && SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0))
+	   && integer_zerop (TREE_OPERAND (base, 1)))
+    {
+      ao_ref refd;
+      bool modified = false;
+
+      *index_p = ipa_get_param_decl_index (info,
+					   SSA_NAME_VAR (TREE_OPERAND (base,								       0)));
+      if (*index_p < 0)
+	return false;
+      ao_ref_init (&refd, base);
+      walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified, &modified,
+			  NULL);
+      if (!modified)
+	{
+	  *agg_contents_p = true;
+	  return true;
+	}
+    }
+
+  return false;
 }
 
 /* See if statement might disappear after inlining.
@@ -1423,6 +1568,8 @@ set_cond_stmt_execution_predicate (struc
   gimple last;
   tree op;
   int index;
+  bool agg_contents;
+  HOST_WIDE_INT offset;
   enum tree_code code, inverted_code;
   edge e;
   edge_iterator ei;
@@ -1441,12 +1588,9 @@ set_cond_stmt_execution_predicate (struc
   /* TODO: handle conditionals like
      var = op0 < 4;
      if (var != 0).  */
-  parm = unmodified_parm (last, op);
-  if (parm)
+  if (unmodified_parm_or_parm_agg_item (info, last, op, &index, &agg_contents,
+					&offset))
     {
-      index = ipa_get_param_decl_index (info, parm);
-      if (index == -1)
-	return;
       code = gimple_cond_code (last);
       inverted_code
 	 = invert_tree_comparison (code,
@@ -1455,7 +1599,7 @@ set_cond_stmt_execution_predicate (struc
       FOR_EACH_EDGE (e, ei, bb->succs)
 	{
 	  struct predicate p = add_condition (summary,
-					      index,
+					      index, agg_contents, offset,
 					      e->flags & EDGE_TRUE_VALUE
 					      ? code : inverted_code,
 					      gimple_cond_rhs (last));
@@ -1481,6 +1625,8 @@ set_cond_stmt_execution_predicate (struc
       || gimple_call_num_args (set_stmt) != 1)
     return;
   op2 = gimple_call_arg (set_stmt, 0);
+  /* !!! I am not sure what the get_base_address is supposed to do here.
+         Still, it might make sense to support aggregate values even here.  */
   base = get_base_address (op2);
   parm = unmodified_parm (set_stmt, base ? base : op2);
   if (!parm)
@@ -1495,7 +1641,7 @@ set_cond_stmt_execution_predicate (struc
     if (e->flags & EDGE_FALSE_VALUE)
       {
 	struct predicate p = add_condition (summary,
-					    index,
+					    index, false, 0,
 					    IS_NOT_CONSTANT,
 					    NULL);
 	e->aux = pool_alloc (edge_predicate_pool);
@@ -1515,22 +1661,20 @@ set_switch_stmt_execution_predicate (str
   gimple last;
   tree op;
   int index;
+  bool agg_contents;
+  HOST_WIDE_INT offset;
   edge e;
   edge_iterator ei;
   size_t n;
   size_t case_idx;
-  tree parm;
 
   last = last_stmt (bb);
   if (!last
       || gimple_code (last) != GIMPLE_SWITCH)
     return;
   op = gimple_switch_index (last);
-  parm = unmodified_parm (last, op);
-  if (!parm)
-    return;
-  index = ipa_get_param_decl_index (info, parm);
-  if (index == -1)
+  if (!unmodified_parm_or_parm_agg_item (info, last, op, &index, &agg_contents,
+					&offset))
     return;
 
   FOR_EACH_EDGE (e, ei, bb->succs)
@@ -1555,16 +1699,16 @@ set_switch_stmt_execution_predicate (str
       if (!min && !max)
 	p = true_predicate ();
       else if (!max)
-	p = add_condition (summary, index,
+	p = add_condition (summary, index, agg_contents, offset,
 			   EQ_EXPR,
 			   min);
       else
 	{
 	  struct predicate p1, p2;
-	  p1 = add_condition (summary, index,
+	  p1 = add_condition (summary, index, agg_contents, offset,
 			      GE_EXPR,
 			      min);
-	  p2 = add_condition (summary, index,
+	  p2 = add_condition (summary, index, agg_contents, offset,
 			      LE_EXPR,
 			      max);
 	  p = and_predicates (summary->conds, &p1, &p2);
@@ -1660,13 +1804,14 @@ will_be_nonconstant_predicate (struct ip
 			       struct inline_summary *summary,
 			       gimple stmt,
 			       VEC (predicate_t, heap) *nonconstant_names)
-			      
 {
   struct predicate p = true_predicate ();
   ssa_op_iter iter;
   tree use;
   struct predicate op_non_const;
-  bool is_load;
+  bool is_load, agg_contents;
+  int base_index;
+  HOST_WIDE_INT offset;
 
   /* What statments might be optimized away
      when their arguments are constant
@@ -1682,31 +1827,27 @@ will_be_nonconstant_predicate (struct ip
     return p;
 
   is_load = gimple_vuse (stmt) != NULL;
-
-  /* Loads can be optimized when the value is known.  */
+   /* Loads can be optimized when the value is known.  */
   if (is_load)
     {
-      tree op = gimple_assign_rhs1 (stmt);
-      tree base = get_base_address (op);
-      tree parm;
-
+      tree op;
       gcc_assert (gimple_assign_single_p (stmt));
-      if (!base)
-	return p;
-      parm = unmodified_parm (stmt, base);
-      if (!parm )
-	return p;
-      if (ipa_get_param_decl_index (info, parm) < 0)
+      op = gimple_assign_rhs1 (stmt);
+      if (!unmodified_parm_or_parm_agg_item (info, stmt, op, &base_index,
+					     &agg_contents, &offset))
 	return p;
     }
+  else
+    base_index = -1;
 
   /* See if we understand all operands before we start
      adding conditionals.  */
   FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
     {
       tree parm = unmodified_parm (stmt, use);
+      int index;
       /* For arguments we can build a condition.  */
-      if (parm && ipa_get_param_decl_index (info, parm) >= 0)
+      if (parm && (index = ipa_get_param_decl_index (info, parm)) >= 0)
 	continue;
       if (TREE_CODE (use) != SSA_NAME)
 	return p;
@@ -1720,20 +1861,21 @@ will_be_nonconstant_predicate (struct ip
   op_non_const = false_predicate ();
   if (is_load)
     {
-      tree parm = unmodified_parm
-		    (stmt, get_base_address (gimple_assign_rhs1 (stmt)));
-      p = add_condition (summary,
-			 ipa_get_param_decl_index (info, parm),
+      p = add_condition (summary, base_index, agg_contents, offset,
 			 CHANGED, NULL);
       op_non_const = or_predicates (summary->conds, &p, &op_non_const);
     }
   FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
     {
       tree parm = unmodified_parm (stmt, use);
-      if (parm && ipa_get_param_decl_index (info, parm) >= 0)
-	p = add_condition (summary,
-			   ipa_get_param_decl_index (info, parm),
-			   CHANGED, NULL);
+      int index;
+
+      if (parm
+	  && (index = ipa_get_param_decl_index (info, parm)) >= 0)
+	{
+	  if (index != base_index)
+	    p = add_condition (summary, index, false, 0, CHANGED, NULL);
+	}
       else
 	p = *VEC_index (predicate_t, nonconstant_names,
 			SSA_NAME_VERSION (use));
@@ -2195,7 +2337,8 @@ static void
 estimate_edge_devirt_benefit (struct cgraph_edge *ie,
 			      int *size, int *time, int prob,
 			      VEC (tree, heap) *known_vals,
-			      VEC (tree, heap) *known_binfos)
+			      VEC (tree, heap) *known_binfos,
+			      VEC (ipa_agg_jump_function_p, heap) *known_aggs)
 {
   tree target;
   int time_diff, size_diff;
@@ -2203,7 +2346,8 @@ estimate_edge_devirt_benefit (struct cgr
   if (!known_vals && !known_binfos)
     return;
 
-  target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos);
+  target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos,
+					 known_aggs);
   if (!target)
     return;
 
@@ -2260,7 +2404,8 @@ static void
 estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time,
 			      clause_t possible_truths,
 			      VEC (tree, heap) *known_vals,
-			      VEC (tree, heap) *known_binfos)
+			      VEC (tree, heap) *known_binfos,
+			      VEC (ipa_agg_jump_function_p, heap) *known_aggs)
 {
   struct cgraph_edge *e;
   for (e = node->callees; e; e = e->next_callee)
@@ -2277,7 +2422,7 @@ estimate_calls_size_and_time (struct cgr
 	  else
 	    estimate_calls_size_and_time (e->callee, size, time,
 					  possible_truths,
-					  known_vals, known_binfos);
+					  known_vals, known_binfos, known_aggs);
 	}
     }
   for (e = node->indirect_calls; e; e = e->next_callee)
@@ -2287,7 +2432,7 @@ estimate_calls_size_and_time (struct cgr
 	{
 	  estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE);
 	  estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE,
-					known_vals, known_binfos);
+					known_vals, known_binfos, known_aggs);
 	}
     }
 }
@@ -2302,6 +2447,7 @@ estimate_node_size_and_time (struct cgra
 			     clause_t possible_truths,
 			     VEC (tree, heap) *known_vals,
 			     VEC (tree, heap) *known_binfos,
+			     VEC (ipa_agg_jump_function_p, heap) *known_aggs,
 		       	     int *ret_size, int *ret_time,
 			     VEC (inline_param_summary_t, heap)
 			       *inline_param_summary)
@@ -2353,7 +2499,7 @@ estimate_node_size_and_time (struct cgra
     time = MAX_TIME * INLINE_TIME_SCALE;
 
   estimate_calls_size_and_time (node, &size, &time, possible_truths,
-				known_vals, known_binfos);
+				known_vals, known_binfos, known_aggs);
   time = (time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE;
   size = (size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE;
 
@@ -2382,13 +2528,12 @@ estimate_ipcp_clone_size_and_time (struc
 {
   clause_t clause;
 
-  clause = evaluate_conditions_for_known_args (node, false, known_vals);
-  estimate_node_size_and_time (node, clause, known_vals, known_binfos,
+  clause = evaluate_conditions_for_known_args (node, false, known_vals, NULL);
+  estimate_node_size_and_time (node, clause, known_vals, known_binfos, NULL,
 			       ret_size, ret_time,
 			       NULL);
 }
 
-
 /* Translate all conditions from callee representation into caller
    representation and symbolically evaluate predicate P into new predicate.
 
@@ -2403,6 +2548,7 @@ remap_predicate (struct inline_summary *
 		 struct inline_summary *callee_info,
 		 struct predicate *p,
 		 VEC (int, heap) *operand_map,
+		 VEC (unsigned, heap) *offset_map,
 		 clause_t possible_truths,
 		 struct predicate *toplev_predicate)
 {
@@ -2437,13 +2583,25 @@ remap_predicate (struct inline_summary *
 		    Otherwise give up.  */
 		 if (!operand_map
 		     || (int)VEC_length (int, operand_map) <= c->operand_num
-		     || VEC_index (int, operand_map, c->operand_num) == -1)
+		     || VEC_index (int, operand_map, c->operand_num) == -1
+		     || (!c->agg_contents
+			 && VEC_index (unsigned, offset_map,
+				       c->operand_num) != 0))
 		   cond_predicate = true_predicate ();
 		 else
-		   cond_predicate = add_condition (info,
-						   VEC_index (int, operand_map,
-							      c->operand_num),
-						   c->code, c->val);
+		   {
+		     HOST_WIDE_INT new_offset;
+
+		     new_offset = c->offset + VEC_index (unsigned, offset_map,
+							 c->operand_num);
+		     cond_predicate = add_condition (info,
+						     VEC_index (int,
+								operand_map,
+								c->operand_num),
+						     c->agg_contents,
+						     new_offset, c->code,
+						     c->val);
+		   }
 	      }
 	    /* Fixed conditions remains same, construct single
 	       condition predicate.  */
@@ -2550,6 +2708,7 @@ remap_edge_summaries  (struct cgraph_edg
 		       struct inline_summary *info,
 		       struct inline_summary *callee_info,
 		       VEC (int, heap) *operand_map,
+		       VEC (unsigned, heap) *offset_map,
 		       clause_t possible_truths,
 		       struct predicate *toplev_predicate)
 {
@@ -2566,7 +2725,8 @@ remap_edge_summaries  (struct cgraph_edg
 	  if (es->predicate)
 	    {
 	      p = remap_predicate (info, callee_info,
-				   es->predicate, operand_map, possible_truths,
+				   es->predicate, operand_map, offset_map,
+				   possible_truths,
 				   toplev_predicate);
 	      edge_set_predicate (e, &p);
 	      /* TODO: We should remove the edge for code that will be
@@ -2583,7 +2743,8 @@ remap_edge_summaries  (struct cgraph_edg
 	}
       else
 	remap_edge_summaries (inlined_edge, e->callee, info, callee_info,
-			      operand_map, possible_truths, toplev_predicate);
+			      operand_map, offset_map, possible_truths,
+			      toplev_predicate);
     }
   for (e = node->indirect_calls; e; e = e->next_callee)
     {
@@ -2594,8 +2755,8 @@ remap_edge_summaries  (struct cgraph_edg
       if (es->predicate)
 	{
 	  p = remap_predicate (info, callee_info,
-			       es->predicate, operand_map, possible_truths,
-			       toplev_predicate);
+			       es->predicate, operand_map, offset_map,
+			       possible_truths, toplev_predicate);
 	  edge_set_predicate (e, &p);
 	  /* TODO: We should remove the edge for code that will be optimized
 	     out, but we need to keep verifiers and tree-inline happy.
@@ -2624,6 +2785,7 @@ inline_merge_summary (struct cgraph_edge
   clause_t clause = 0;		/* not_inline is known to be false.  */
   size_time_entry *e;
   VEC (int, heap) *operand_map = NULL;
+  VEC (unsigned, heap) *offset_map = NULL;
   int i;
   struct predicate toplev_predicate;
   struct predicate true_p = true_predicate ();
@@ -2640,9 +2802,12 @@ inline_merge_summary (struct cgraph_edge
       int count = ipa_get_cs_argument_count (args);
       int i;
 
-      evaluate_properties_for_edge (edge, true, &clause, NULL, NULL);
+      evaluate_properties_for_edge (edge, true, &clause, NULL, NULL, NULL);
       if (count)
-	VEC_safe_grow_cleared (int, heap, operand_map, count);
+	{
+	  VEC_safe_grow_cleared (int, heap, operand_map, count);
+	  VEC_safe_grow_cleared (unsigned, heap, offset_map, count);
+	}
       for (i = 0; i < count; i++)
 	{
 	  struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, i);
@@ -2651,6 +2816,15 @@ inline_merge_summary (struct cgraph_edge
 	  if (jfunc->type == IPA_JF_PASS_THROUGH
 	      && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
 	    map = ipa_get_jf_pass_through_formal_id (jfunc);
+	  else if (jfunc->type == IPA_JF_ANCESTOR)
+	    {
+	      HOST_WIDE_INT offset = ipa_get_jf_ancestor_offset (jfunc);
+	      if (offset >= 0 && offset < UINT_MAX)
+		{
+		  map = ipa_get_jf_ancestor_formal_id (jfunc);
+		  VEC_replace (unsigned, offset_map, i, (unsigned) offset);
+		}
+	    }
 	  VEC_replace (int, operand_map, i, map);
 	  gcc_assert (map < ipa_get_param_count (IPA_NODE_REF (to)));
 	}
@@ -2658,7 +2832,8 @@ inline_merge_summary (struct cgraph_edge
   for (i = 0; VEC_iterate (size_time_entry, callee_info->entry, i, e); i++)
     {
       struct predicate p = remap_predicate (info, callee_info,
-					    &e->predicate, operand_map, clause,
+					    &e->predicate, operand_map,
+					    offset_map, clause,
 					    &toplev_predicate);
       if (!false_predicate_p (&p))
 	{
@@ -2680,14 +2855,14 @@ inline_merge_summary (struct cgraph_edge
 	}
     }
   remap_edge_summaries (edge, edge->callee, info, callee_info, operand_map,
-			clause, &toplev_predicate);
+			offset_map, clause, &toplev_predicate);
   info->size = 0;
   info->time = 0;
   for (i = 0; VEC_iterate (size_time_entry, info->entry, i, e); i++)
     info->size += e->size, info->time += e->time;
   estimate_calls_size_and_time (to, &info->size, &info->time,
 				~(clause_t)(1 << predicate_false_condition),
-				NULL, NULL);
+				NULL, NULL, NULL);
 
   inline_update_callee_summaries (edge->callee,
 				  inline_edge_summary (edge)->loop_depth);
@@ -2697,6 +2872,7 @@ inline_merge_summary (struct cgraph_edge
   /* Similarly remove param summaries.  */
   VEC_free (inline_param_summary_t, heap, es->param);
   VEC_free (int, heap, operand_map);
+  VEC_free (unsigned, heap, offset_map);
 
   info->time = (info->time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE;
   info->size = (info->size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE;
@@ -2720,17 +2896,20 @@ do_estimate_edge_time (struct cgraph_edg
   clause_t clause;
   VEC (tree, heap) *known_vals;
   VEC (tree, heap) *known_binfos;
+  VEC (ipa_agg_jump_function_p, heap) *known_aggs;
   struct inline_edge_summary *es = inline_edge_summary (edge);
 
   callee = cgraph_function_or_thunk_node (edge->callee, NULL);
 
   gcc_checking_assert (edge->inline_failed);
   evaluate_properties_for_edge (edge, true,
-				&clause, &known_vals, &known_binfos);
+				&clause, &known_vals, &known_binfos,
+				&known_aggs);
   estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
-			       &size, &time, es->param);
+			       known_aggs, &size, &time, es->param);
   VEC_free (tree, heap, known_vals);
   VEC_free (tree, heap, known_binfos);
+  VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
 
   ret = (((gcov_type)time
 	   - es->call_stmt_time) * edge->frequency
@@ -2767,6 +2946,7 @@ do_estimate_edge_growth (struct cgraph_e
   clause_t clause;
   VEC (tree, heap) *known_vals;
   VEC (tree, heap) *known_binfos;
+  VEC (ipa_agg_jump_function_p, heap) *known_aggs;
 
   /* When we do caching, use do_estimate_edge_time to populate the entry.  */
 
@@ -2785,11 +2965,13 @@ do_estimate_edge_growth (struct cgraph_e
   /* Early inliner runs without caching, go ahead and do the dirty work.  */
   gcc_checking_assert (edge->inline_failed);
   evaluate_properties_for_edge (edge, true,
-				&clause, &known_vals, &known_binfos);
+				&clause, &known_vals, &known_binfos,
+				&known_aggs);
   estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
-			       &size, NULL, NULL);
+			       known_aggs, &size, NULL, NULL);
   VEC_free (tree, heap, known_vals);
   VEC_free (tree, heap, known_binfos);
+  VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
   gcc_checking_assert (inline_edge_summary (edge)->call_stmt_size);
   return size - inline_edge_summary (edge)->call_stmt_size;
 }
@@ -3069,6 +3251,10 @@ inline_read_section (struct lto_file_dec
 	  c.operand_num = streamer_read_uhwi (&ib);
 	  c.code = (enum tree_code) streamer_read_uhwi (&ib);
 	  c.val = stream_read_tree (&ib, data_in);
+	  bp = streamer_read_bitpack (&ib);
+	  c.agg_contents = bp_unpack_value (&bp, 1);
+	  if (c.agg_contents)
+	    c.offset = streamer_read_uhwi (&ib);
 	  VEC_safe_push (condition, gc, info->conds, &c);
 	}
       count2 = streamer_read_uhwi (&ib);
@@ -3212,6 +3398,11 @@ inline_write_summary (cgraph_node_set se
 	      streamer_write_uhwi (ob, c->operand_num);
 	      streamer_write_uhwi (ob, c->code);
 	      stream_write_tree (ob, c->val, true);
+	      bp = bitpack_create (ob->main_stream);
+	      bp_pack_value (&bp, c->agg_contents, 1);
+	      streamer_write_bitpack (&bp);
+	      if (c->agg_contents)
+		streamer_write_uhwi (ob, c->offset);
 	    }
 	  streamer_write_uhwi (ob, VEC_length (size_time_entry, info->entry));
 	  for (i = 0;
Index: src/gcc/ipa-cp.c
===================================================================
--- src.orig/gcc/ipa-cp.c
+++ src/gcc/ipa-cp.c
@@ -1087,7 +1087,8 @@ propagate_constants_accross_call (struct
 tree
 ipa_get_indirect_edge_target (struct cgraph_edge *ie,
 			      VEC (tree, heap) *known_vals,
-			      VEC (tree, heap) *known_binfos)
+			      VEC (tree, heap) *known_binfos,
+			      VEC (ipa_agg_jump_function_p, heap) *known_aggs)
 {
   int param_index = ie->indirect_info->param_index;
   HOST_WIDE_INT token, anc_offset;
@@ -1099,8 +1100,26 @@ ipa_get_indirect_edge_target (struct cgr
 
   if (!ie->indirect_info->polymorphic)
     {
-      tree t = (VEC_length (tree, known_vals) > (unsigned int) param_index
-	        ? VEC_index (tree, known_vals, param_index) : NULL);
+      tree t;
+
+      if (ie->indirect_info->agg_contents)
+	{
+	  if (VEC_length (ipa_agg_jump_function_p, known_aggs)
+	      > (unsigned int) param_index)
+	    {
+	      struct ipa_agg_jump_function *agg;
+	      agg = VEC_index (ipa_agg_jump_function_p, known_aggs,
+			       param_index);
+	      t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
+					      ie->caller, param_index);
+	    }
+	  else
+	    t = NULL;
+	}
+      else
+	t = (VEC_length (tree, known_vals) > (unsigned int) param_index
+	     ? VEC_index (tree, known_vals, param_index) : NULL);
+
       if (t &&
 	  TREE_CODE (t) == ADDR_EXPR
 	  && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL)
@@ -1109,6 +1128,7 @@ ipa_get_indirect_edge_target (struct cgr
 	return NULL_TREE;
     }
 
+  gcc_assert (!ie->indirect_info->agg_contents);
   token = ie->indirect_info->otr_token;
   anc_offset = ie->indirect_info->offset;
   otr_type = ie->indirect_info->otr_type;
@@ -1159,7 +1179,8 @@ devirtualization_time_bonus (struct cgra
       struct inline_summary *isummary;
       tree target;
 
-      target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos);
+      target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos,
+					     NULL);
       if (!target)
 	continue;
 
@@ -1676,7 +1697,7 @@ ipcp_discover_new_direct_edges (struct c
       tree target;
 
       next_ie = ie->next_callee;
-      target = ipa_get_indirect_edge_target (ie, known_vals, NULL);
+      target = ipa_get_indirect_edge_target (ie, known_vals, NULL, NULL);
       if (target)
 	ipa_make_edge_direct_to_target (ie, target);
     }
Index: src/gcc/ipa-prop.h
===================================================================
--- src.orig/gcc/ipa-prop.h
+++ src/gcc/ipa-prop.h
@@ -500,8 +500,9 @@ bool ipa_propagate_indirect_call_infos (
 
 /* Indirect edge and binfo processing.  */
 tree ipa_get_indirect_edge_target (struct cgraph_edge *ie,
-				   VEC (tree, heap) *known_csts,
-				   VEC (tree, heap) *known_binfs);
+				   VEC (tree, heap) *,
+				   VEC (tree, heap) *,
+				   VEC (ipa_agg_jump_function_p, heap) *);
 struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree);
 
 /* Functions related to both.  */

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

* [PATCH 2/5] Build aggregate jump functions
  2012-06-01  1:24 [PATCH 0/5] Aggregate jump functions Martin Jambor
  2012-06-01  1:24 ` [PATCH 5/5] Compute predicates for phi node results in ipa-inline-analysis.c Martin Jambor
  2012-06-01  1:24 ` [PATCH 4/5] Incorporate aggregate jump functions to inlining analysis Martin Jambor
@ 2012-06-01  1:24 ` Martin Jambor
  2012-06-01 11:33   ` Richard Guenther
  2012-06-01  1:24 ` [PATCH 1/5] Access methods for " Martin Jambor
  2012-06-01  1:25 ` [PATCH 3/5] Use aggregate jump functions in indirect inlining Martin Jambor
  4 siblings, 1 reply; 8+ messages in thread
From: Martin Jambor @ 2012-06-01  1:24 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka

[-- Attachment #1: agg_jump_functions.diff --]
[-- Type: text/plain, Size: 42148 bytes --]

Hi,

this patch adds the capability to build aggregate jump functions.
These consist of:

1) information what known compile time IPA-invariants are at various
   offsets of an aggregate passed to a callee either by reference
   (when the parameter is pointer) or by value (when it is an
   aggregate).  This patch simply scans the current BB backwards from
   the call statement and examines what values are stored there.  Even
   this simple approach does not look particularly simple
   (determine_known_aggregate_parts), it is however usually sufficient
   for Fortran array descriptions (in the testcases I have seen).  For
   more advanced uses like devirtualization we'll need something that
   will examine the whole function but will look quite like this, only
   even more messy, I'm afraid.

   When we do this, we also record the type through which data was
   stored into the aggregate, which is either the type of a DECL or
   type stored in the offset of a  MEM_REF.

2) Being able to conservatively but usefully recognize that an
   aggregate (passed either by reference or a value) that we got from
   a caller and pass it to a callee has not changed.

   This is slightly complex in cases where aggregates are passed by
   reference.  Because in gimple pointer types carry more-or-less no
   information about the data it points to, we'd normally have to feed
   AA with a type that aliases all in order to get conservatively
   correct results.  That might however be too much conservative.  We
   circumvent this problem by feeding AA the pointed-to type but also
   verifying that the aggregate data was stored with the same type (or
   a type containing the pointed-to type at the expected offset).

   The data structures can also represent such pass-through functions
   with known listed exceptions but that is not currently implemented
   and is left for later.

However, this patch does not use the collected data in any way, that
is what two subsequent patches do.

The patch passes bootstrap and testing on x86_64-linux.

Martin



2012-05-30  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.c (param_analysis_info): Fields modified and
	visited_statements rename to parm_modified and parm_visited_statements
	respectively, added fields ref_modified and ref_visited_statements.
	(ipa_print_node_jump_functions_for_edge): Dump agg_preserved flags and
	aggregate jump functions.
	(ipa_set_jf_simple_pass_through): Set also agg_preserved.
	(ipa_set_ancestor_jf): Likewise.
	(ipa_set_jf_arith_pass_through): Clear agg_preserved.
	(is_parm_modified_before_stmt): Logic reversed, renamed to
	is_parm_preserved_before_stmt.  All callers updated.
	(is_parm_ref_data_preserved): New function.
	(compute_complex_assign_jump_func): Check if aggregate contents are
	preserved.
	(compute_complex_ancestor_jump_func): Likewise.
	(compute_scalar_jump_functions): Removed.
	(compute_pass_through_member_ptrs): Likewise.
	(compute_cst_member_ptr_arguments): Likewise.
	(init_ao_ref_for_byref_agg_jf): New function.
	(ipa_known_agg_contents_list): New type.
	(determine_known_aggregate_parts): New function.
	(ipa_compute_jump_functions_for_edge): Compute all kinds of jump
	functions (scalar, aggregate and member pointer).
	(ipa_analyze_node): Initialize new fields of param_analysis_info.
	(ipa_agg_types_propagatable_p): New function.
	(update_jump_functions_after_inlining): Handle aggregate contents.
	(ipa_edge_duplication_hook): Copy also aggregate jump functions.
	(ipa_write_jump_function): Stream agg_preserved flags and aggregate
	jump functions.
	(ipa_read_jump_function): Likewise.

	* ipa-prop.h (ipa_pass_through_data): New field agg_preserved.
	(ipa_ancestor_jf_data): Likewise.
	(ipa_agg_jf_item): New type.
	(ipa_agg_jump_function): Likewise.
	(ipa_jump_func): New field agg.
	(ipa_get_jf_pass_through_agg_preserved): New function.
	(ipa_get_jf_ancestor_agg_preserved): Likewise.

Index: src/gcc/ipa-prop.h
===================================================================
--- src.orig/gcc/ipa-prop.h
+++ src/gcc/ipa-prop.h
@@ -104,6 +104,13 @@ struct GTY(()) ipa_pass_through_data
      arithmetic operation where the caller's parameter is the first operand and
      operand field from this structure is the second one.  */
   enum tree_code operation;
+  /* When the passed value is a pointer or an aggregate passed by value, it is
+     set to true only when we are certain that no write to the object it points
+     to or the aggregate has occurred since the caller functions started
+     execution, except for changes noted in the aggregate part of the jump
+     function (see description of ipa_agg_jump_function).  The flag is used
+     only when this operation is NOP_EXPR.  */
+  bool agg_preserved;
 };
 
 /* Structure holding data required to describe an ancestor pass-through
@@ -117,6 +124,8 @@ struct GTY(()) ipa_ancestor_jf_data
   tree type;
   /* Number of the caller's formal parameter being passed.  */
   int formal_id;
+  /* Flag with the same meaning like agg_preserve in ipa_pass_through_data.  */
+  bool agg_preserved;
 };
 
 /* Structure holding a C++ member pointer constant.  Holds a pointer to the
@@ -127,11 +136,53 @@ struct GTY(()) ipa_member_ptr_cst
   tree delta;
 };
 
+/* An element in an aggegate part of a jump function describing a known value
+   at a given offset.  When the this 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
+   fullfill is_gimple_ip_invariant.  */
+
+typedef struct GTY(()) ipa_agg_jf_item
+{
+  /* The offset at which the known value is located within the aggregate.  */
+  HOST_WIDE_INT offset;
+
+  /* The known constant or type if this is a clobber.  */
+  tree value;
+} ipa_agg_jf_item_t;
+
+DEF_VEC_O (ipa_agg_jf_item_t);
+DEF_VEC_ALLOC_O (ipa_agg_jf_item_t, gc);
+
+/* Aggregate jump function - i.e. description of contents of aggregates passed
+   either by reference or value.  */
+
+struct GTY(()) ipa_agg_jump_function
+{
+  /* Description of the individual items.  */
+  VEC (ipa_agg_jf_item_t, gc) *items;
+  /* Type which was used to store the contents of the aggregate.  Not
+     meaningful if items is NULL. */
+  tree type;
+};
+
+typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
+DEF_VEC_P (ipa_agg_jump_function_p);
+DEF_VEC_ALLOC_P (ipa_agg_jump_function_p, heap);
+
 /* A jump function for a callsite represents the values passed as actual
    arguments of the callsite. See enum jump_func_type for the various
    types of jump functions supported.  */
 typedef struct GTY (()) ipa_jump_func
 {
+  /* Aggregate contants description.  See struct ipa_agg_jump_function and its
+     description.  */
+  struct ipa_agg_jump_function agg;
+
   enum jump_func_type type;
   /* Represents a value of a jump function.  pass_through is used only in jump
      function context.  constant represents the actual constant in constant jump
@@ -214,6 +265,15 @@ ipa_get_jf_pass_through_operation (struc
   return jfunc->value.pass_through.operation;
 }
 
+/* Return the agg_preserved flag of a pass through jump functin JFUNC.  */
+
+static inline bool
+ipa_get_jf_pass_through_agg_preserved (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_PASS_THROUGH);
+  return jfunc->value.pass_through.agg_preserved;
+}
+
 /* Return the offset of an ancestor jump function JFUNC.  */
 
 static inline HOST_WIDE_INT
@@ -242,6 +302,15 @@ ipa_get_jf_ancestor_formal_id (struct ip
   return jfunc->value.ancestor.formal_id;
 }
 
+/* Return the agg_preserved flag of an ancestor jump functin JFUNC.  */
+
+static inline bool
+ipa_get_jf_ancestor_agg_preserved (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR);
+  return jfunc->value.ancestor.agg_preserved;
+}
+
 /* Return the pfn part of a member pointer constant jump function JFUNC.  */
 
 static inline tree
Index: src/gcc/ipa-prop.c
===================================================================
--- src.orig/gcc/ipa-prop.c
+++ src/gcc/ipa-prop.c
@@ -47,8 +47,9 @@ along with GCC; see the file COPYING3.
 
 struct param_analysis_info
 {
-  bool modified;
-  bitmap visited_statements;
+  bool parm_modified, ref_modified;
+  bitmap parm_visited_statements, ref_visited_statements;
+  tree deref;
 };
 
 /* Vector where the parameter infos are actually stored. */
@@ -192,13 +193,18 @@ ipa_print_node_jump_functions_for_edge (
       else if (type == IPA_JF_PASS_THROUGH)
 	{
 	  fprintf (f, "PASS THROUGH: ");
-	  fprintf (f, "%d, op %s ",
+	  fprintf (f, "%d, op %s",
 		   jump_func->value.pass_through.formal_id,
 		   tree_code_name[(int)
 				  jump_func->value.pass_through.operation]);
 	  if (jump_func->value.pass_through.operation != NOP_EXPR)
-	    print_generic_expr (f,
-				jump_func->value.pass_through.operand, 0);
+	    {
+	      fprintf (f, " ");
+	      print_generic_expr (f,
+				  jump_func->value.pass_through.operand, 0);
+	    }
+	  if (jump_func->value.pass_through.agg_preserved)
+	    fprintf (f, ", agg_preserved");
 	  fprintf (f, "\n");
 	}
       else if (type == IPA_JF_ANCESTOR)
@@ -208,8 +214,33 @@ ipa_print_node_jump_functions_for_edge (
 		   jump_func->value.ancestor.formal_id,
 		   jump_func->value.ancestor.offset);
 	  print_generic_expr (f, jump_func->value.ancestor.type, 0);
+	  if (jump_func->value.ancestor.agg_preserved)
+	    fprintf (f, ", agg_preserved");
 	  fprintf (f, "\n");
 	}
+
+      if (jump_func->agg.items)
+	{
+	  struct ipa_agg_jf_item *item;
+	  int j;
+
+	  fprintf (f, "         Aggregte contents:\n");
+	  FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, 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_low_cst (TYPE_SIZE (item->value), 1));
+	      else
+		{
+		  fprintf (f, "cst: ");
+		  print_generic_expr (f, item->value, 0);
+		}
+	      fprintf (f, "\n");
+	    }
+	}
     }
 }
 
@@ -289,12 +320,14 @@ ipa_set_jf_constant (struct ipa_jump_fun
 
 /* Set JFUNC to be a simple pass-through jump function.  */
 static void
-ipa_set_jf_simple_pass_through (struct ipa_jump_func *jfunc, int formal_id)
+ipa_set_jf_simple_pass_through (struct ipa_jump_func *jfunc, int formal_id,
+				bool agg_preserved)
 {
   jfunc->type = IPA_JF_PASS_THROUGH;
   jfunc->value.pass_through.operand = NULL_TREE;
   jfunc->value.pass_through.formal_id = formal_id;
   jfunc->value.pass_through.operation = NOP_EXPR;
+  jfunc->value.pass_through.agg_preserved = agg_preserved;
 }
 
 /* Set JFUNC to be an arithmetic pass through jump function.  */
@@ -307,18 +340,20 @@ ipa_set_jf_arith_pass_through (struct ip
   jfunc->value.pass_through.operand = operand;
   jfunc->value.pass_through.formal_id = formal_id;
   jfunc->value.pass_through.operation = operation;
+  jfunc->value.pass_through.agg_preserved = false;
 }
 
 /* Set JFUNC to be an ancestor jump function.  */
 
 static void
 ipa_set_ancestor_jf (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
-		     tree type, int formal_id)
+		     tree type, int formal_id, bool agg_preserved)
 {
   jfunc->type = IPA_JF_ANCESTOR;
   jfunc->value.ancestor.formal_id = formal_id;
   jfunc->value.ancestor.offset = offset;
   jfunc->value.ancestor.type = type;
+  jfunc->value.ancestor.agg_preserved = agg_preserved;
 }
 
 /* Simple function filling in a member pointer constant jump function (with PFN
@@ -584,30 +619,65 @@ mark_modified (ao_ref *ao ATTRIBUTE_UNUS
   return true;
 }
 
-/* Return true if the formal parameter PARM might have been modified in this
+/* Return true if the formal parameter PARM is known not to be modified in this
    function before reaching the statement STMT.  PARM_AINFO is a pointer to a
    structure containing temporary information about PARM.  */
 
 static bool
-is_parm_modified_before_stmt (struct param_analysis_info *parm_ainfo,
-			      gimple stmt, tree parm)
+is_parm_preserved_before_stmt (struct param_analysis_info *parm_ainfo,
+			       gimple stmt, tree parm)
 {
   bool modified = false;
   ao_ref refd;
 
-  if (parm_ainfo->modified)
-    return true;
+  if (parm_ainfo->parm_modified)
+    return false;
 
   gcc_checking_assert (gimple_vuse (stmt) != NULL_TREE);
   ao_ref_init (&refd, parm);
   walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified,
-		      &modified, &parm_ainfo->visited_statements);
+		      &modified, &parm_ainfo->parm_visited_statements);
   if (modified)
     {
-      parm_ainfo->modified = true;
-      return true;
+      parm_ainfo->parm_modified = true;
+      return false;
     }
-  return false;
+  return true;
+}
+
+/* Return true if the data pointed to by PARM are known to be unmodified in
+   this function before reaching statement STMT.  PARM_AINFO is a pointer to a
+   structure containing temporary information about PARM.  */
+
+static bool
+is_parm_ref_data_preserved (struct param_analysis_info *parm_ainfo,
+			    gimple stmt, tree parm)
+{
+  bool modified = false;
+  ao_ref refd;
+
+  if (parm_ainfo->ref_modified
+      || !gimple_vuse (stmt)
+      || !POINTER_TYPE_P (TREE_TYPE (parm))
+      || !AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (parm))))
+    return false;
+
+  if (!parm_ainfo->deref)
+    /* Use of this alias type requires that we make sure the data we pass to
+       the function is actually of the supposed type.  But we do that both when
+       gathering the contents of aggregates and when propagating.  */
+    parm_ainfo->deref = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (parm)), parm,
+				build_int_cst (TREE_TYPE (parm), 0));
+
+  ao_ref_init (&refd, parm_ainfo->deref);
+  walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified,
+		      &modified, &parm_ainfo->ref_visited_statements);
+  if (modified)
+    {
+      parm_ainfo->ref_modified = true;
+      return false;
+    }
+  return true;
 }
 
 /* If STMT is an assignment that loads a value from an parameter declaration,
@@ -631,7 +701,7 @@ load_from_unmodified_param (struct ipa_n
 
   index = ipa_get_param_decl_index (info, op1);
   if (index < 0
-      || is_parm_modified_before_stmt (&parms_ainfo[index], stmt, op1))
+      || !is_parm_preserved_before_stmt (&parms_ainfo[index], stmt, op1))
     return -1;
 
   return index;
@@ -734,7 +804,11 @@ compute_complex_assign_jump_func (struct
 	}
       else if (gimple_assign_single_p (stmt)
 	       && !detect_type_change_ssa (tc_ssa, call, jfunc))
-	ipa_set_jf_simple_pass_through (jfunc, index);
+	{
+	  bool agg_p = is_parm_ref_data_preserved (&parms_ainfo[index],
+						   call, tc_ssa);
+	  ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
+	}
       return;
     }
 
@@ -760,7 +834,9 @@ compute_complex_assign_jump_func (struct
   index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa));
   if (index >= 0
       && !detect_type_change (op1, base, call, jfunc, offset))
-    ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (op1), index);
+    ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (op1), index,
+			 is_parm_ref_data_preserved (&parms_ainfo[index],
+						     call, ssa));
 }
 
 /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
@@ -831,6 +907,7 @@ get_ancestor_addr_info (gimple assign, t
 
 static void
 compute_complex_ancestor_jump_func (struct ipa_node_params *info,
+				    struct param_analysis_info *parms_ainfo,
 				    struct ipa_jump_func *jfunc,
 				    gimple call, gimple phi)
 {
@@ -884,7 +961,9 @@ compute_complex_ancestor_jump_func (stru
     }
 
   if (!detect_type_change (obj, expr, call, jfunc, offset))
-    ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index);
+    ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index,
+			 is_parm_ref_data_preserved (&parms_ainfo[index],
+						     call, parm));
 }
 
 /* Given OP which is passed as an actual argument to a called function,
@@ -919,55 +998,6 @@ compute_known_type_jump_func (tree op, s
   ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base), TREE_TYPE (op));
 }
 
-
-/* Determine the jump functions of scalar arguments.  Scalar means SSA names
-   and constants of a number of selected types.  INFO is the ipa_node_params
-   structure associated with the caller, PARMS_AINFO describes state of
-   analysis with respect to individual formal parameters.  ARGS is the
-   ipa_edge_args structure describing the callsite CALL which is the call
-   statement being examined.*/
-
-static void
-compute_scalar_jump_functions (struct ipa_node_params *info,
-			       struct param_analysis_info *parms_ainfo,
-			       struct ipa_edge_args *args,
-			       gimple call)
-{
-  tree arg;
-  unsigned num = 0;
-
-  for (num = 0; num < gimple_call_num_args (call); num++)
-    {
-      struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, num);
-      arg = gimple_call_arg (call, num);
-
-      if (is_gimple_ip_invariant (arg))
-	ipa_set_jf_constant (jfunc, arg);
-      else if (TREE_CODE (arg) == SSA_NAME)
-	{
-	  if (SSA_NAME_IS_DEFAULT_DEF (arg))
-	    {
-	      int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg));
-
-	      if (index >= 0
-		  && !detect_type_change_ssa (arg, call, jfunc))
-		ipa_set_jf_simple_pass_through (jfunc, index);
-	    }
-	  else
-	    {
-	      gimple stmt = SSA_NAME_DEF_STMT (arg);
-	      if (is_gimple_assign (stmt))
-		compute_complex_assign_jump_func (info, parms_ainfo, jfunc,
-						  call, stmt, arg);
-	      else if (gimple_code (stmt) == GIMPLE_PHI)
-		compute_complex_ancestor_jump_func (info, jfunc, call, stmt);
-	    }
-	}
-      else
-	compute_known_type_jump_func (arg, jfunc, call);
-    }
-}
-
 /* Inspect the given TYPE and return true iff it has the same structure (the
    same number of fields of the same types) as a C++ member pointer.  If
    METHOD_PTR and DELTA are non-NULL, store the trees representing the
@@ -1001,54 +1031,9 @@ type_like_member_ptr_p (tree type, tree
   return true;
 }
 
-/* Go through arguments of the CALL and for every one that looks like a member
-   pointer, check whether it can be safely declared pass-through and if so,
-   mark that to the corresponding item of jump FUNCTIONS.  Return true iff
-   there are non-pass-through member pointers within the arguments.  INFO
-   describes formal parameters of the caller.  PARMS_INFO is a pointer to a
-   vector containing intermediate information about each formal parameter.  */
-
-static bool
-compute_pass_through_member_ptrs (struct ipa_node_params *info,
-				  struct param_analysis_info *parms_ainfo,
-				  struct ipa_edge_args *args,
-				  gimple call)
-{
-  bool undecided_members = false;
-  unsigned num;
-  tree arg;
-
-  for (num = 0; num < gimple_call_num_args (call); num++)
-    {
-      arg = gimple_call_arg (call, num);
-
-      if (type_like_member_ptr_p (TREE_TYPE (arg), NULL, NULL))
-	{
-	  if (TREE_CODE (arg) == PARM_DECL)
-	    {
-	      int index = ipa_get_param_decl_index (info, arg);
-
-	      gcc_assert (index >=0);
-	      if (!is_parm_modified_before_stmt (&parms_ainfo[index], call,
-						 arg))
-		{
-		  struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args,
-								       num);
-		  ipa_set_jf_simple_pass_through (jfunc, index);
-		}
-	      else
-		undecided_members = true;
-	    }
-	  else
-	    undecided_members = true;
-	}
-    }
-
-  return undecided_members;
-}
-
 /* If RHS is an SSA_NAME and it is defined by a simple copy assign statement,
-   return the rhs of its defining statement.  */
+   return the rhs of its defining statement.  Otherwise return RHS as it
+   is.  */
 
 static inline tree
 get_ssa_def_if_simple_copy (tree rhs)
@@ -1142,27 +1127,235 @@ determine_cst_member_ptr (gimple call, t
   return;
 }
 
-/* Go through the arguments of the CALL and for every member pointer within
-   tries determine whether it is a constant.  If it is, create a corresponding
-   constant jump function in FUNCTIONS which is an array of jump functions
-   associated with the call.  */
+/* Helper for determine_known_aggregate_parts, initializes *R for an aggregate
+   passed by reference based on BASE and with the given TYPE.  */
 
 static void
-compute_cst_member_ptr_arguments (struct ipa_edge_args *args,
-				  gimple call)
+init_ao_ref_for_byref_agg_jf (ao_ref *r, tree base, tree type)
+{
+  static tree alias_all_type = NULL;
+
+  if (!alias_all_type)
+    alias_all_type = build_pointer_type_for_mode (char_type_node,
+						  ptr_mode, true);
+  ao_ref_init (r, build2 (MEM_REF, type, base,
+			  build_int_cst (alias_all_type, 0)));
+}
+
+/* TODO: Turn this into a PARAM.  */
+#define IPA_MAX_AFF_JF_ITEMS 16
+
+/* Simple linked list, describing known contents of an aggregate beforere
+   call.  */
+
+struct ipa_known_agg_contents_list
 {
-  unsigned num;
-  tree arg, method_field, delta_field;
+  /* 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;
+  /* Pointer to the next structure in the list.  */
+  struct ipa_known_agg_contents_list *next;
+};
+
+/* 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.  JFUNC is the jump function into
+   which the constants are subsequently stored.  */
+
+static void
+determine_known_aggregate_parts (gimple call, tree arg,
+				 struct ipa_jump_func *jfunc)
+{
+  struct ipa_known_agg_contents_list *list = NULL;
+  int item_count = 0, const_count = 0;
+  HOST_WIDE_INT arg_offset, arg_size;
+  gimple_stmt_iterator gsi;
+  tree arg_base;
+  bool by_ref;
+  ao_ref r;
+
+  /* The function operates in three stages.  First, we prepare r, arg_base and
+     arg_offset based on what is actually passed as an actual argument.  */
 
-  for (num = 0; num < gimple_call_num_args (call); num++)
+  if (POINTER_TYPE_P (TREE_TYPE (arg)))
     {
-      struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, num);
-      arg = gimple_call_arg (call, num);
+      gcc_checking_assert (AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (arg))));
+      if (TREE_CODE (arg) == SSA_NAME)
+	{
+	  if (!host_integerp (TYPE_SIZE (TREE_TYPE (TREE_TYPE (arg))), 1))
+	    return;
 
-      if (jfunc->type == IPA_JF_UNKNOWN
-	  && type_like_member_ptr_p (TREE_TYPE (arg), &method_field,
-				     &delta_field))
-	determine_cst_member_ptr (call, arg, method_field, delta_field, jfunc);
+	  by_ref = true;
+	  arg_base = arg;
+	  arg_offset = 0;
+	  arg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (TREE_TYPE (arg))), 1);
+	  init_ao_ref_for_byref_agg_jf (&r, arg_base,
+					TREE_TYPE (TREE_TYPE (arg)));
+	}
+      else if (TREE_CODE (arg) == ADDR_EXPR)
+	{
+	  HOST_WIDE_INT arg_max_size;
+
+	  arg = TREE_OPERAND (arg, 0);
+	  arg_base = get_ref_base_and_extent (arg, &arg_offset, &arg_size,
+					  &arg_max_size);
+	  if (arg_max_size == -1
+	      || arg_max_size != arg_size
+	      || arg_offset < 0)
+	    return;
+	  if (DECL_P (arg_base))
+	    {
+	      by_ref = false;
+	      ao_ref_init (&r, arg);
+	    }
+	  else
+	    return;
+	}
+      else
+	return;
+    }
+  else
+    {
+      HOST_WIDE_INT arg_max_size;
+
+      gcc_checking_assert (AGGREGATE_TYPE_P (TREE_TYPE (arg)));
+
+      by_ref = false;
+      arg_base = get_ref_base_and_extent (arg, &arg_offset, &arg_size,
+					  &arg_max_size);
+      if (arg_max_size == -1
+	  || arg_max_size != arg_size
+	  || arg_offset < 0)
+	return;
+
+      ao_ref_init (&r, arg);
+    }
+
+  /* Second stage walks back the BB, looks at individual statements and as long
+     as it is confident of how the statements affect contents of the
+     aggregates, it builds a sorted linked list of ipa_agg_jf_list structures
+     describing it.  */
+  gsi = gsi_for_stmt (call);
+  gsi_prev (&gsi);
+  for (; !gsi_end_p (gsi); gsi_prev (&gsi))
+    {
+      struct ipa_known_agg_contents_list *n, **p;
+      gimple stmt = gsi_stmt (gsi);
+      HOST_WIDE_INT lhs_offset, lhs_size, lhs_max_size;
+      tree lhs, rhs, lhs_base;
+      bool partial_overlap;
+
+      if (!stmt_may_clobber_ref_p_1 (stmt, &r))
+	continue;
+      if (!gimple_assign_single_p (stmt))
+	break;
+
+      lhs = gimple_assign_lhs (stmt);
+      rhs = gimple_assign_rhs1 (stmt);
+      if (!is_gimple_reg_type (rhs))
+	break;
+
+      lhs_base = get_ref_base_and_extent (lhs, &lhs_offset, &lhs_size,
+					  &lhs_max_size);
+      if (lhs_max_size == -1
+	  || lhs_max_size != lhs_size
+	  || (lhs_offset < arg_offset
+	      && lhs_offset + lhs_size > arg_offset)
+	  || (lhs_offset < arg_offset + arg_size
+	      && lhs_offset + lhs_size > arg_offset + arg_size))
+	break;
+
+      if (by_ref)
+	{
+	  if (TREE_CODE (lhs_base) != MEM_REF
+	      || TREE_OPERAND (lhs_base, 0) != arg_base
+	      || !integer_zerop (TREE_OPERAND (lhs_base, 1))
+	      /* Pointers do not reliably carry type information, make sure the
+		 memory is accessed as the type it is supposed to be because in
+		 PASS_THROUGH jump functions we feed the type to alias
+		 analysis.  */
+	      || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (
+						 TREE_OPERAND (lhs_base, 1))))
+		  != TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (arg_base)))))
+	    break;
+	}
+      else if (lhs_base != arg_base)
+	break;
+
+      if (lhs_offset + lhs_size < arg_offset
+	  || lhs_offset >= (arg_offset + arg_size))
+	continue;
+
+      partial_overlap = false;
+      p = &list;
+      while (*p && (*p)->offset < lhs_offset)
+	{
+	  if (*p && (*p)->offset + (*p)->size > lhs_offset)
+	    {
+	      partial_overlap = true;
+	      break;
+	    }
+	  p = &(*p)->next;
+	}
+      if (partial_overlap)
+	break;
+      if (*p && (*p)->offset < lhs_offset + lhs_size)
+	{
+	  if ((*p)->offset == lhs_offset && (*p)->size == lhs_size)
+	    /* We already know this value is subsequently overwritten with
+	       something else.  */
+	    continue;
+	  else
+	    /* Otherwise this is a partial overlap which we cannot
+	       represent.  */
+	    break;
+	}
+
+      rhs = get_ssa_def_if_simple_copy (rhs);
+      n = XALLOCA (struct ipa_known_agg_contents_list);
+      n->size = lhs_size;
+      n->offset = lhs_offset;
+      if (is_gimple_ip_invariant (rhs))
+	{
+	  n->constant = rhs;
+	  const_count++;
+	}
+      else
+	n->constant = NULL_TREE;
+      n->next = *p;
+      *p = n;
+
+      item_count++;
+      if (const_count == IPA_MAX_AFF_JF_ITEMS
+	  || item_count == 2 * IPA_MAX_AFF_JF_ITEMS)
+	break;
+    }
+
+  /* Third stage just goes over the list and creates an appropriate vector of
+     ipa_agg_jf_item structures out of it, of sourse only if there are
+     any known constants to begin with.  */
+
+  if (const_count)
+    {
+      jfunc->agg.items = VEC_alloc (ipa_agg_jf_item_t, gc, const_count);
+      if (POINTER_TYPE_P (TREE_TYPE (arg_base)))
+	jfunc->agg.type = TREE_TYPE (TREE_TYPE (arg_base));
+      else
+	jfunc->agg.type = TREE_TYPE (arg_base);
+      gcc_checking_assert (AGGREGATE_TYPE_P (jfunc->agg.type));
+      while (list)
+	{
+	  if (list->constant)
+	    {
+	      struct ipa_agg_jf_item *item;
+	      item = VEC_quick_push (ipa_agg_jf_item_t,
+				     jfunc->agg.items, NULL);
+	      item->offset = list->offset - arg_offset;
+	      item->value = list->constant;
+	    }
+	  list = list->next;
+	}
     }
 }
 
@@ -1177,23 +1370,84 @@ ipa_compute_jump_functions_for_edge (str
   struct ipa_node_params *info = IPA_NODE_REF (cs->caller);
   struct ipa_edge_args *args = IPA_EDGE_REF (cs);
   gimple call = cs->call_stmt;
-  int arg_num = gimple_call_num_args (call);
+  int n, arg_num = gimple_call_num_args (call);
 
   if (arg_num == 0 || args->jump_functions)
     return;
   VEC_safe_grow_cleared (ipa_jump_func_t, gc, args->jump_functions, arg_num);
 
-  /* We will deal with constants and SSA scalars first:  */
-  compute_scalar_jump_functions (info, parms_ainfo, args, call);
+  for (n = 0; n < arg_num; n++)
+    {
+      struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, n);
+      tree arg = gimple_call_arg (call, n);
 
-  /* Let's check whether there are any potential member pointers and if so,
-     whether we can determine their functions as pass_through.  */
-  if (!compute_pass_through_member_ptrs (info, parms_ainfo, args, call))
-    return;
+      if (is_gimple_ip_invariant (arg))
+	ipa_set_jf_constant (jfunc, arg);
+      else if (!is_gimple_reg_type (TREE_TYPE (arg)))
+	{
+	  tree method_field, delta_field;
 
-  /* Finally, let's check whether we actually pass a new constant member
-     pointer here...  */
-  compute_cst_member_ptr_arguments (args, call);
+	  /* Aggregate passed by value, check for pass-through, otherwise fill
+	     in aggregate contents.  */
+
+	  if (TREE_CODE (arg) == PARM_DECL)
+	    {
+	      int index = ipa_get_param_decl_index (info, arg);
+	      gcc_assert (index >=0);
+	      if (is_parm_preserved_before_stmt (&parms_ainfo[index], call,
+						 arg))
+		{
+		  ipa_set_jf_simple_pass_through (jfunc, index, true);
+		  continue;
+		}
+	    }
+
+	  /* TODO: The call to determine_cst_member_ptr will be removed by a
+	     subsequent patch which will do away with IPA_JF_CONST_MEMBER_PTR
+	     altogether.  */
+	  if (type_like_member_ptr_p (TREE_TYPE (arg), &method_field,
+				      &delta_field))
+	    determine_cst_member_ptr (call, arg, method_field, delta_field,
+				      jfunc);
+	}
+      else if (TREE_CODE (arg) == SSA_NAME)
+	{
+	  if (SSA_NAME_IS_DEFAULT_DEF (arg))
+	    {
+	      int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg));
+	      if (index >= 0
+		  && !detect_type_change_ssa (arg, call, jfunc))
+		{
+		  bool agg_p;
+		  agg_p = is_parm_ref_data_preserved (&parms_ainfo[index],
+						      call, arg);
+		  ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
+		}
+	    }
+	  else
+	    {
+	      gimple stmt = SSA_NAME_DEF_STMT (arg);
+	      if (is_gimple_assign (stmt))
+		compute_complex_assign_jump_func (info, parms_ainfo, jfunc,
+						  call, stmt, arg);
+	      else if (gimple_code (stmt) == GIMPLE_PHI)
+		compute_complex_ancestor_jump_func (info, parms_ainfo, jfunc,
+						    call, stmt);
+	    }
+	}
+      else
+	compute_known_type_jump_func (arg, jfunc, call);
+
+      if ((jfunc->type != IPA_JF_PASS_THROUGH
+	      || !ipa_get_jf_pass_through_agg_preserved (jfunc))
+	  && (jfunc->type != IPA_JF_ANCESTOR
+	      || !ipa_get_jf_ancestor_agg_preserved (jfunc))
+	  && jfunc->type != IPA_JF_CONST_MEMBER_PTR
+	  && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
+	      || (POINTER_TYPE_P (TREE_TYPE (arg))
+		  && AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (arg))))))
+	determine_known_aggregate_parts (call, arg, jfunc);
+    }
 }
 
 /* Compute jump functions for all edges - both direct and indirect - outgoing
@@ -1486,7 +1740,7 @@ ipa_analyze_indirect_call_uses (struct c
     return;
 
   index = ipa_get_param_decl_index (info, rec);
-  if (index >= 0 && !is_parm_modified_before_stmt (&parms_ainfo[index],
+  if (index >= 0 && is_parm_preserved_before_stmt (&parms_ainfo[index],
 						   call, rec))
     ipa_note_param_call (node, index, call);
 
@@ -1685,8 +1939,12 @@ ipa_analyze_node (struct cgraph_node *no
   ipa_compute_jump_functions (node, parms_ainfo);
 
   for (i = 0; i < param_count; i++)
-    if (parms_ainfo[i].visited_statements)
-      BITMAP_FREE (parms_ainfo[i].visited_statements);
+    {
+      if (parms_ainfo[i].parm_visited_statements)
+	BITMAP_FREE (parms_ainfo[i].parm_visited_statements);
+      if (parms_ainfo[i].ref_visited_statements)
+	BITMAP_FREE (parms_ainfo[i].ref_visited_statements);
+    }
 
   current_function_decl = NULL;
   pop_cfun ();
@@ -1713,6 +1971,68 @@ combine_known_type_and_ancestor_jfs (str
 			 combined_type);
 }
 
+/* Return true if we can still rely on our non-modification analysis
+   (is_parm_preserved_before_stmt and especially is_parm_ref_data_preserved) if
+   information about aggregate contents AGG can be propagated to or through a
+   parameter of type TO.  If OFFSET is zero, we look at the types as they are
+   first.  Then, if FROM is a record or a pointer to a record, we look for a
+   TO-typed field at offset.  */
+
+static bool
+ipa_agg_types_propagatable_p (struct ipa_agg_jump_function *agg,
+			      tree to, HOST_WIDE_INT offset)
+{
+  tree from = agg->type;
+
+  gcc_assert (AGGREGATE_TYPE_P (from));
+  gcc_checking_assert (TYPE_P (to));
+  if (POINTER_TYPE_P (to))
+    to = TREE_TYPE (to);
+
+  if (offset == 0 && useless_type_conversion_p (to, from))
+    return true;
+
+  /* If the above failed, we resort to finding a field within a structure,
+     provided we are looking at one.  */
+  if (TREE_CODE (from) != RECORD_TYPE)
+    return false;
+
+  while (true)
+    {
+      tree fld;
+
+      for (fld = TYPE_FIELDS (from); fld; fld = DECL_CHAIN (fld))
+	{
+	  HOST_WIDE_INT pos, size;
+	  tree tr_size;
+
+	  if (TREE_CODE (fld) != FIELD_DECL)
+	    continue;
+
+	  pos = int_bit_position (fld);
+	  tr_size = DECL_SIZE (fld);
+	  if (!tr_size || !host_integerp (tr_size, 1))
+	    continue;
+	  size = tree_low_cst (tr_size, 1);
+	  if (pos > offset
+	      || (pos + size) <= offset)
+	    continue;
+
+	  from = TREE_TYPE (fld);
+	  if (offset == pos
+	      && TYPE_MAIN_VARIANT (from) == TYPE_MAIN_VARIANT (to))
+	    return true;
+
+	  if (TREE_CODE (from) == RECORD_TYPE)
+	    break;
+	  else
+	    return false;
+	}
+      if (!fld)
+	return false;
+    }
+}
+
 /* Update the jump functions associated with call graph edge E when the call
    graph edge CS is being inlined, assuming that E->caller is already (possibly
    indirectly) inlined into CS->callee and that E has not been inlined.  */
@@ -1733,26 +2053,52 @@ update_jump_functions_after_inlining (st
       if (dst->type == IPA_JF_ANCESTOR)
 	{
 	  struct ipa_jump_func *src;
+	  int dst_fid = dst->value.ancestor.formal_id;
 
 	  /* Variable number of arguments can cause havoc if we try to access
 	     one that does not exist in the inlined edge.  So make sure we
 	     don't.  */
-	  if (dst->value.ancestor.formal_id >= ipa_get_cs_argument_count (top))
+	  if (dst_fid >= ipa_get_cs_argument_count (top))
 	    {
 	      dst->type = IPA_JF_UNKNOWN;
 	      continue;
 	    }
 
-	  src = ipa_get_ith_jump_func (top, dst->value.ancestor.formal_id);
+	  src = ipa_get_ith_jump_func (top, dst_fid);
+
+	  if (dst->value.ancestor.agg_preserved && src->agg.items)
+	    {
+	      struct ipa_node_params *info = IPA_NODE_REF (cs->callee);
+	      tree t = TREE_TYPE (ipa_get_param (info, dst_fid));
+
+	      /* Currently we do not produce clobber aggregate jump functions,
+		 replace with merging when we do.  */
+	      gcc_assert (!dst->agg.items);
+
+	      if (ipa_agg_types_propagatable_p (&src->agg, t,
+						dst->value.ancestor.offset))
+		{
+		  dst->agg.items = VEC_copy (ipa_agg_jf_item_t, gc,
+					     src->agg.items);
+		  dst->agg.type = src->agg.type;
+		}
+	    }
+
 	  if (src->type == IPA_JF_KNOWN_TYPE)
 	    combine_known_type_and_ancestor_jfs (src, dst);
 	  else if (src->type == IPA_JF_PASS_THROUGH
 		   && src->value.pass_through.operation == NOP_EXPR)
-	    dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
+	    {
+	      dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
+	      dst->value.ancestor.agg_preserved
+		&= src->value.pass_through.agg_preserved;
+	    }
 	  else if (src->type == IPA_JF_ANCESTOR)
 	    {
 	      dst->value.ancestor.formal_id = src->value.ancestor.formal_id;
 	      dst->value.ancestor.offset += src->value.ancestor.offset;
+	      dst->value.ancestor.agg_preserved
+		&= src->value.ancestor.agg_preserved;
 	    }
 	  else
 	    dst->type = IPA_JF_UNKNOWN;
@@ -1766,9 +2112,39 @@ update_jump_functions_after_inlining (st
 	      && (dst->value.pass_through.formal_id
 		  < ipa_get_cs_argument_count (top)))
 	    {
-	      src = ipa_get_ith_jump_func (top,
-					   dst->value.pass_through.formal_id);
-	      *dst = *src;
+	      bool agg_p;
+	      int dst_fid = dst->value.pass_through.formal_id;
+	      src = ipa_get_ith_jump_func (top, dst_fid);
+	      agg_p = dst->value.pass_through.agg_preserved;
+	      /* Currently we do not produce clobber aggregate jump functions,
+		 replace with merging when we do.  */
+	      gcc_assert (!agg_p || !dst->agg.items);
+
+	      dst->type = src->type;
+	      dst->value = src->value;
+
+	      if (agg_p)
+		{
+		  if (src->agg.items)
+		    {
+		      struct ipa_node_params *info = IPA_NODE_REF (cs->callee);
+		      tree t = TREE_TYPE (ipa_get_param (info, dst_fid));
+
+		      if (ipa_agg_types_propagatable_p (&src->agg, t, 0))
+			{
+			  dst->agg.items = VEC_copy (ipa_agg_jf_item_t, gc,
+						     src->agg.items);
+			  dst->agg.type = src->agg.type;
+			}
+		    }
+		}
+	      else
+		{
+		  if (dst->type == IPA_JF_PASS_THROUGH)
+		    dst->value.pass_through.agg_preserved = 0;
+		  else if (dst->type == IPA_JF_ANCESTOR)
+		    dst->value.ancestor.agg_preserved = 0;
+		}
 	    }
 	  else
 	    dst->type = IPA_JF_UNKNOWN;
@@ -2077,13 +2453,14 @@ ipa_node_removal_hook (struct cgraph_nod
   ipa_free_node_params_substructures (IPA_NODE_REF (node));
 }
 
-/* Hook that is called by cgraph.c when a node is duplicated.  */
+/* Hook that is called by cgraph.c when an edge is duplicated.  */
 
 static void
 ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst,
 			   __attribute__((unused)) void *data)
 {
   struct ipa_edge_args *old_args, *new_args;
+  unsigned int i;
 
   ipa_check_create_edge_args ();
 
@@ -2092,6 +2469,12 @@ ipa_edge_duplication_hook (struct cgraph
 
   new_args->jump_functions = VEC_copy (ipa_jump_func_t, gc,
 				       old_args->jump_functions);
+
+  for (i = 0; i < VEC_length (ipa_jump_func_t, old_args->jump_functions); i++)
+    VEC_index (ipa_jump_func_t, new_args->jump_functions, i)->agg.items
+      = VEC_copy (ipa_agg_jf_item_t, gc,
+		  VEC_index (ipa_jump_func_t,
+			     old_args->jump_functions, i)->agg.items);
 }
 
 /* Hook that is called by cgraph.c when a node is duplicated.  */
@@ -2790,8 +3173,12 @@ static void
 ipa_write_jump_function (struct output_block *ob,
 			 struct ipa_jump_func *jump_func)
 {
-  streamer_write_uhwi (ob, jump_func->type);
+  struct ipa_agg_jf_item *item;
+  struct bitpack_d bp;
+  unsigned count;
+  int i;
 
+  streamer_write_uhwi (ob, jump_func->type);
   switch (jump_func->type)
     {
     case IPA_JF_UNKNOWN:
@@ -2808,17 +3195,33 @@ ipa_write_jump_function (struct output_b
       stream_write_tree (ob, jump_func->value.pass_through.operand, true);
       streamer_write_uhwi (ob, jump_func->value.pass_through.formal_id);
       streamer_write_uhwi (ob, jump_func->value.pass_through.operation);
+      bp = bitpack_create (ob->main_stream);
+      bp_pack_value (&bp, jump_func->value.pass_through.agg_preserved, 1);
+      streamer_write_bitpack (&bp);
       break;
     case IPA_JF_ANCESTOR:
       streamer_write_uhwi (ob, jump_func->value.ancestor.offset);
       stream_write_tree (ob, jump_func->value.ancestor.type, true);
       streamer_write_uhwi (ob, jump_func->value.ancestor.formal_id);
+      bp = bitpack_create (ob->main_stream);
+      bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1);
+      streamer_write_bitpack (&bp);
       break;
     case IPA_JF_CONST_MEMBER_PTR:
       stream_write_tree (ob, jump_func->value.member_cst.pfn, true);
       stream_write_tree (ob, jump_func->value.member_cst.delta, false);
       break;
     }
+
+  count = VEC_length (ipa_agg_jf_item_t, jump_func->agg.items);
+  streamer_write_uhwi (ob, count);
+  if (count)
+    stream_write_tree (ob, jump_func->agg.type, true);
+  FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, jump_func->agg.items, i, item)
+    {
+      streamer_write_uhwi (ob, item->offset);
+      stream_write_tree (ob, item->value, true);
+    }
 }
 
 /* Read in jump function JUMP_FUNC from IB.  */
@@ -2828,8 +3231,10 @@ ipa_read_jump_function (struct lto_input
 			struct ipa_jump_func *jump_func,
 			struct data_in *data_in)
 {
-  jump_func->type = (enum jump_func_type) streamer_read_uhwi (ib);
+  struct bitpack_d bp;
+  int i, count;
 
+  jump_func->type = (enum jump_func_type) streamer_read_uhwi (ib);
   switch (jump_func->type)
     {
     case IPA_JF_UNKNOWN:
@@ -2848,17 +3253,34 @@ ipa_read_jump_function (struct lto_input
       jump_func->value.pass_through.formal_id = streamer_read_uhwi (ib);
       jump_func->value.pass_through.operation
 	= (enum tree_code) streamer_read_uhwi (ib);
+      bp = streamer_read_bitpack (ib);
+      jump_func->value.pass_through.agg_preserved = bp_unpack_value (&bp, 1);
       break;
     case IPA_JF_ANCESTOR:
       jump_func->value.ancestor.offset = streamer_read_uhwi (ib);
       jump_func->value.ancestor.type = stream_read_tree (ib, data_in);
       jump_func->value.ancestor.formal_id = streamer_read_uhwi (ib);
+      bp = streamer_read_bitpack (ib);
+      jump_func->value.ancestor.agg_preserved = bp_unpack_value (&bp, 1);
       break;
     case IPA_JF_CONST_MEMBER_PTR:
       jump_func->value.member_cst.pfn = stream_read_tree (ib, data_in);
       jump_func->value.member_cst.delta = stream_read_tree (ib, data_in);
       break;
     }
+
+  count = streamer_read_uhwi (ib);
+  jump_func->agg.items = VEC_alloc (ipa_agg_jf_item_t, gc, count);
+  if (count)
+    jump_func->agg.type = stream_read_tree (ib, data_in);
+  for (i = 0; i < count; i++)
+    {
+      struct ipa_agg_jf_item *item = VEC_quick_push (ipa_agg_jf_item_t,
+				       jump_func->agg.items, NULL);
+
+      item->offset = streamer_read_uhwi (ib);
+      item->value = stream_read_tree (ib, data_in);
+    }
 }
 
 /* Stream out parts of cgraph_indirect_call_info corresponding to CS that are

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

* [PATCH 1/5] Access methods for jump functions
  2012-06-01  1:24 [PATCH 0/5] Aggregate jump functions Martin Jambor
                   ` (2 preceding siblings ...)
  2012-06-01  1:24 ` [PATCH 2/5] Build aggregate jump functions Martin Jambor
@ 2012-06-01  1:24 ` Martin Jambor
  2012-06-01  8:49   ` Jan Hubicka
  2012-06-01  1:25 ` [PATCH 3/5] Use aggregate jump functions in indirect inlining Martin Jambor
  4 siblings, 1 reply; 8+ messages in thread
From: Martin Jambor @ 2012-06-01  1:24 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka

[-- Attachment #1: jf_access_methods.diff --]
[-- Type: text/plain, Size: 23749 bytes --]

Hi,

the first patch mainly introduces access methods to read parts of jump
functions.  I am no particular fan of these but the are now more
widely spread than originally and above all checking asserts verifying
that the correct part of the union is read should really be useful.

I have also unified creation of jump function in ipa-prop.c to new
functions so that all fields are initialized.  On the contrary, I have
left update_jump_functions_after_inlining largely alone because it
modifies jump functions in place (and there may be good reason not to
touch some parts in the future, if not now), dumping functions and
streaming because they are a bit special and simple too.

There is also a tiny amount of other cleanup.

Bootstrapped separately on x86_64-linux.  OK for trunk?

Thanks,

Martin



2012-05-03  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.h (ipa_get_jf_known_type_offset): New function.
	(ipa_get_jf_known_type_base_type): Likewise.
	(ipa_get_jf_known_type_component_type): Likewise.
	(ipa_get_jf_constant): Likewise.
	(ipa_get_jf_pass_through_formal_id): Likewise.
	(ipa_get_jf_pass_through_operation): Likewise.
	(ipa_get_jf_ancestor_offset): Likewise.
	(ipa_get_jf_ancestor_type): Likewise.
	(ipa_get_jf_ancestor_formal_id): Likewise.
	(ipa_get_jf_member_ptr_pfn): Likewise.

	* ipa-prop.c (ipa_set_jf_known_type): New function.
	(ipa_set_jf_constant): Likewise.
	(ipa_set_jf_simple_pass_through): Likewise.
	(ipa_set_jf_arith_pass_through): Likewise.
	(ipa_set_ancestor_jf): Likewise.
	(fill_member_ptr_cst_jump_function): Moved up and renamed to
	ipa_set_jf_member_ptr_cst.
	(detect_type_change_1): Use the new jump function creation functions.
	(compute_complex_assign_jump_func): Likewise.
	(compute_complex_ancestor_jump_func): Likewise.
	(compute_known_type_jump_func): Likewise.
	(compute_scalar_jump_functions): Likewise.
	(compute_pass_through_member_ptrs): Likewise.
	(determine_cst_member_ptr): Likewise.
	(combine_known_type_and_ancestor_jfs): Likewise.
	(try_make_edge_direct_simple_call): Likewise.
	(try_make_edge_direct_virtual_call): Likewise.
	(update_indirect_edges_after_inlining): Likewise.

	* ipa-cp.c (ipa_get_jf_pass_through_result): Use jump function
	access functions.  Incorporat NOP_EXPR and BINFO handling from its
	callers.
	(ipa_get_jf_ancestor_result): Likewise.  Incorporate handling BINFOs
	which was in its callers.
	(ipa_value_from_jfunc): Use jump function access functions.  Some
	functionality moved to functions above.
	(propagate_vals_accross_ancestor): Likewise.
	(propagate_vals_accross_pass_through): Use jump function access
	functions.
	(propagate_accross_jump_function): Likewise.

	* ipa-inline-analysis.c (remap_edge_change_prob): Use jump function
	access functions.
	(inline_merge_summary): Likewise.

Index: src/gcc/ipa-cp.c
===================================================================
--- src.orig/gcc/ipa-cp.c
+++ src/gcc/ipa-cp.c
@@ -638,17 +638,19 @@ ipa_get_jf_pass_through_result (struct i
 {
   tree restype, res;
 
-  gcc_checking_assert (is_gimple_ip_invariant (input));
-  if (jfunc->value.pass_through.operation == NOP_EXPR)
+  if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
     return input;
+  else if (TREE_CODE (input) == TREE_BINFO)
+    return NULL_TREE;
 
-  if (TREE_CODE_CLASS (jfunc->value.pass_through.operation)
+  gcc_checking_assert (is_gimple_ip_invariant (input));
+  if (TREE_CODE_CLASS (ipa_get_jf_pass_through_operation (jfunc))
       == tcc_comparison)
     restype = boolean_type_node;
   else
     restype = TREE_TYPE (input);
-  res = fold_binary (jfunc->value.pass_through.operation, restype,
-		     input, jfunc->value.pass_through.operand);
+  res = fold_binary (ipa_get_jf_pass_through_operation (jfunc), restype,
+		     input, ipa_get_jf_pass_through_operand (jfunc));
 
   if (res && !is_gimple_ip_invariant (res))
     return NULL_TREE;
@@ -662,12 +664,16 @@ ipa_get_jf_pass_through_result (struct i
 static tree
 ipa_get_jf_ancestor_result (struct ipa_jump_func *jfunc, tree input)
 {
-  if (TREE_CODE (input) == ADDR_EXPR)
+  if (TREE_CODE (input) == TREE_BINFO)
+    return get_binfo_at_offset (input,
+				ipa_get_jf_ancestor_offset (jfunc),
+				ipa_get_jf_ancestor_type (jfunc));
+  else if (TREE_CODE (input) == ADDR_EXPR)
     {
       tree t = TREE_OPERAND (input, 0);
       t = build_ref_for_offset (EXPR_LOCATION (t), t,
-				jfunc->value.ancestor.offset,
-				jfunc->value.ancestor.type, NULL, false);
+				ipa_get_jf_ancestor_offset (jfunc),
+				ipa_get_jf_ancestor_type (jfunc), NULL, false);
       return build_fold_addr_expr (t);
     }
   else
@@ -680,12 +686,12 @@ ipa_get_jf_ancestor_result (struct ipa_j
 static tree
 ipa_value_from_known_type_jfunc (struct ipa_jump_func *jfunc)
 {
-  tree base_binfo = TYPE_BINFO (jfunc->value.known_type.base_type);
+  tree base_binfo = TYPE_BINFO (ipa_get_jf_known_type_base_type (jfunc));
   if (!base_binfo)
     return NULL_TREE;
   return get_binfo_at_offset (base_binfo,
-			      jfunc->value.known_type.offset,
-			      jfunc->value.known_type.component_type);
+			      ipa_get_jf_known_type_offset (jfunc),
+			      ipa_get_jf_known_type_component_type (jfunc));
 }
 
 /* Determine whether JFUNC evaluates to a known value (that is either a
@@ -697,7 +703,7 @@ tree
 ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc)
 {
   if (jfunc->type == IPA_JF_CONST)
-    return jfunc->value.constant;
+    return ipa_get_jf_constant (jfunc);
   else if (jfunc->type == IPA_JF_KNOWN_TYPE)
     return ipa_value_from_known_type_jfunc (jfunc);
   else if (jfunc->type == IPA_JF_PASS_THROUGH
@@ -707,9 +713,9 @@ ipa_value_from_jfunc (struct ipa_node_pa
       int idx;
 
       if (jfunc->type == IPA_JF_PASS_THROUGH)
-	idx = jfunc->value.pass_through.formal_id;
+	idx = ipa_get_jf_pass_through_formal_id (jfunc);
       else
-	idx = jfunc->value.ancestor.formal_id;
+	idx = ipa_get_jf_ancestor_formal_id (jfunc);
 
       if (info->ipcp_orig_node)
 	input = VEC_index (tree, info->known_vals, idx);
@@ -732,22 +738,9 @@ ipa_value_from_jfunc (struct ipa_node_pa
 	return NULL_TREE;
 
       if (jfunc->type == IPA_JF_PASS_THROUGH)
-	{
-	  if (jfunc->value.pass_through.operation == NOP_EXPR)
-	    return input;
-	  else if (TREE_CODE (input) == TREE_BINFO)
-	    return NULL_TREE;
-	  else
-	    return ipa_get_jf_pass_through_result (jfunc, input);
-	}
+	return ipa_get_jf_pass_through_result (jfunc, input);
       else
-	{
-	  if (TREE_CODE (input) == TREE_BINFO)
-	    return get_binfo_at_offset (input, jfunc->value.ancestor.offset,
-					jfunc->value.ancestor.type);
-	  else
-	    return ipa_get_jf_ancestor_result (jfunc, input);
-	}
+	return ipa_get_jf_ancestor_result (jfunc, input);
     }
   else
     return NULL_TREE;
@@ -907,13 +900,13 @@ propagate_vals_accross_pass_through (str
   struct ipcp_value *src_val;
   bool ret = false;
 
-  if (jfunc->value.pass_through.operation == NOP_EXPR)
+  if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
     for (src_val = src_lat->values; src_val; src_val = src_val->next)
       ret |= add_value_to_lattice (dest_lat, src_val->value, cs,
 				   src_val, src_idx);
   /* Do not create new values when propagating within an SCC because if there
-     arithmetic functions with circular dependencies, there is infinite number
-     of them and we would just make lattices bottom.  */
+     are arithmetic functions with circular dependencies, there is infinite
+     number of them and we would just make lattices bottom.  */
   else if (edge_within_scc (cs))
     ret = set_lattice_contains_variable (dest_lat);
   else
@@ -956,13 +949,7 @@ propagate_vals_accross_ancestor (struct
 
   for (src_val = src_lat->values; src_val; src_val = src_val->next)
     {
-      tree t = src_val->value;
-
-      if (TREE_CODE (t) == TREE_BINFO)
-	t = get_binfo_at_offset (t, jfunc->value.ancestor.offset,
-				 jfunc->value.ancestor.type);
-      else
-	t = ipa_get_jf_ancestor_result (jfunc, t);
+      tree t = ipa_get_jf_ancestor_result (jfunc, src_val->value);
 
       if (t)
 	ret |= add_value_to_lattice (dest_lat, t, cs, src_val, src_idx);
@@ -996,7 +983,7 @@ propagate_accross_jump_function (struct
 	    return set_lattice_contains_variable (dest_lat);
 	}
       else
-	val = jfunc->value.constant;
+	val = ipa_get_jf_constant (jfunc);
       return add_value_to_lattice (dest_lat, val, cs, NULL, 0);
     }
   else if (jfunc->type == IPA_JF_PASS_THROUGH
@@ -1008,9 +995,9 @@ propagate_accross_jump_function (struct
       bool ret;
 
       if (jfunc->type == IPA_JF_PASS_THROUGH)
-	src_idx = jfunc->value.pass_through.formal_id;
+	src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
       else
-	src_idx = jfunc->value.ancestor.formal_id;
+	src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
 
       src_lat = ipa_get_lattice (caller_info, src_idx);
       if (src_lat->bottom)
Index: src/gcc/ipa-inline-analysis.c
===================================================================
--- src.orig/gcc/ipa-inline-analysis.c
+++ src/gcc/ipa-inline-analysis.c
@@ -2514,16 +2514,16 @@ remap_edge_change_prob (struct cgraph_ed
 	{
 	  struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, i);
 	  if (jfunc->type == IPA_JF_PASS_THROUGH
-	      && (jfunc->value.pass_through.formal_id
+	      && (ipa_get_jf_pass_through_formal_id (jfunc)
 		  < (int) VEC_length (inline_param_summary_t,
-				      inlined_es->param)))
+						   inlined_es->param)))
 	    {
+	      int jf_formal_id = ipa_get_jf_pass_through_formal_id (jfunc);
 	      int prob1 = VEC_index (inline_param_summary_t,
 				     es->param, i)->change_prob;
 	      int prob2 = VEC_index
 			     (inline_param_summary_t,
-			     inlined_es->param,
-			     jfunc->value.pass_through.formal_id)->change_prob;
+			     inlined_es->param, jf_formal_id)->change_prob;
 	      int prob = ((prob1 * prob2 + REG_BR_PROB_BASE / 2)
 			  / REG_BR_PROB_BASE);
 
@@ -2649,8 +2649,8 @@ inline_merge_summary (struct cgraph_edge
 	  int map = -1;
 	  /* TODO: handle non-NOPs when merging.  */
 	  if (jfunc->type == IPA_JF_PASS_THROUGH
-	      && jfunc->value.pass_through.operation == NOP_EXPR)
-	    map = jfunc->value.pass_through.formal_id;
+	      && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
+	    map = ipa_get_jf_pass_through_formal_id (jfunc);
 	  VEC_replace (int, operand_map, i, map);
 	  gcc_assert (map < ipa_get_param_count (IPA_NODE_REF (to)));
 	}
Index: src/gcc/ipa-prop.c
===================================================================
--- src.orig/gcc/ipa-prop.c
+++ src/gcc/ipa-prop.c
@@ -266,6 +266,73 @@ ipa_print_all_jump_functions (FILE *f)
     }
 }
 
+/* Set JFUNC to be a known type jump function.  */
+
+static void
+ipa_set_jf_known_type (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
+		       tree base_type, tree component_type)
+{
+  jfunc->type = IPA_JF_KNOWN_TYPE;
+  jfunc->value.known_type.offset = offset,
+  jfunc->value.known_type.base_type = base_type;
+  jfunc->value.known_type.component_type = component_type;
+}
+
+/* Set JFUNC to be a constant jmp function.  */
+
+static void
+ipa_set_jf_constant (struct ipa_jump_func *jfunc, tree constant)
+{
+  jfunc->type = IPA_JF_CONST;
+  jfunc->value.constant = constant;
+}
+
+/* Set JFUNC to be a simple pass-through jump function.  */
+static void
+ipa_set_jf_simple_pass_through (struct ipa_jump_func *jfunc, int formal_id)
+{
+  jfunc->type = IPA_JF_PASS_THROUGH;
+  jfunc->value.pass_through.operand = NULL_TREE;
+  jfunc->value.pass_through.formal_id = formal_id;
+  jfunc->value.pass_through.operation = NOP_EXPR;
+}
+
+/* Set JFUNC to be an arithmetic pass through jump function.  */
+
+static void
+ipa_set_jf_arith_pass_through (struct ipa_jump_func *jfunc, int formal_id,
+			       tree operand, enum tree_code operation)
+{
+  jfunc->type = IPA_JF_PASS_THROUGH;
+  jfunc->value.pass_through.operand = operand;
+  jfunc->value.pass_through.formal_id = formal_id;
+  jfunc->value.pass_through.operation = operation;
+}
+
+/* Set JFUNC to be an ancestor jump function.  */
+
+static void
+ipa_set_ancestor_jf (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
+		     tree type, int formal_id)
+{
+  jfunc->type = IPA_JF_ANCESTOR;
+  jfunc->value.ancestor.formal_id = formal_id;
+  jfunc->value.ancestor.offset = offset;
+  jfunc->value.ancestor.type = type;
+}
+
+/* Simple function filling in a member pointer constant jump function (with PFN
+   and DELTA as the constant value) into JFUNC.  */
+
+static void
+ipa_set_jf_member_ptr_cst (struct ipa_jump_func *jfunc,
+			   tree pfn, tree delta)
+{
+  jfunc->type = IPA_JF_CONST_MEMBER_PTR;
+  jfunc->value.member_cst.pfn = pfn;
+  jfunc->value.member_cst.delta = delta;
+}
+
 /* Structure to be passed in between detect_type_change and
    check_stmt_for_type_change.  */
 
@@ -464,11 +531,7 @@ detect_type_change_1 (tree arg, tree bas
       || offset != 0)
     jfunc->type = IPA_JF_UNKNOWN;
   else
-    {
-      jfunc->type = IPA_JF_KNOWN_TYPE;
-      jfunc->value.known_type.base_type = tci.known_current_type;
-      jfunc->value.known_type.component_type = comp_type;
-    }
+    ipa_set_jf_known_type (jfunc, 0, tci.known_current_type, comp_type);
 
   return true;
 }
@@ -666,18 +729,12 @@ compute_complex_assign_jump_func (struct
 						 TREE_TYPE (op1))))
 	    return;
 
-	  jfunc->type = IPA_JF_PASS_THROUGH;
-	  jfunc->value.pass_through.formal_id = index;
-	  jfunc->value.pass_through.operation = gimple_assign_rhs_code (stmt);
-	  jfunc->value.pass_through.operand = op2;
+	  ipa_set_jf_arith_pass_through (jfunc, index, op2,
+					 gimple_assign_rhs_code (stmt));
 	}
       else if (gimple_assign_single_p (stmt)
 	       && !detect_type_change_ssa (tc_ssa, call, jfunc))
-	{
-	  jfunc->type = IPA_JF_PASS_THROUGH;
-	  jfunc->value.pass_through.formal_id = index;
-	  jfunc->value.pass_through.operation = NOP_EXPR;
-	}
+	ipa_set_jf_simple_pass_through (jfunc, index);
       return;
     }
 
@@ -703,12 +760,7 @@ compute_complex_assign_jump_func (struct
   index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa));
   if (index >= 0
       && !detect_type_change (op1, base, call, jfunc, offset))
-    {
-      jfunc->type = IPA_JF_ANCESTOR;
-      jfunc->value.ancestor.formal_id = index;
-      jfunc->value.ancestor.offset = offset;
-      jfunc->value.ancestor.type = TREE_TYPE (op1);
-    }
+    ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (op1), index);
 }
 
 /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
@@ -832,12 +884,7 @@ compute_complex_ancestor_jump_func (stru
     }
 
   if (!detect_type_change (obj, expr, call, jfunc, offset))
-    {
-      jfunc->type = IPA_JF_ANCESTOR;
-      jfunc->value.ancestor.formal_id = index;
-      jfunc->value.ancestor.offset = offset;
-      jfunc->value.ancestor.type = TREE_TYPE (obj);
-    }
+    ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index);
 }
 
 /* Given OP which is passed as an actual argument to a called function,
@@ -869,10 +916,7 @@ compute_known_type_jump_func (tree op, s
       || !TYPE_BINFO (TREE_TYPE (base)))
     return;
 
-  jfunc->type = IPA_JF_KNOWN_TYPE;
-  jfunc->value.known_type.base_type = TREE_TYPE (base);
-  jfunc->value.known_type.offset = offset;
-  jfunc->value.known_type.component_type = TREE_TYPE (op);
+  ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base), TREE_TYPE (op));
 }
 
 
@@ -898,10 +942,7 @@ compute_scalar_jump_functions (struct ip
       arg = gimple_call_arg (call, num);
 
       if (is_gimple_ip_invariant (arg))
-	{
-	  jfunc->type = IPA_JF_CONST;
-	  jfunc->value.constant = arg;
-	}
+	ipa_set_jf_constant (jfunc, arg);
       else if (TREE_CODE (arg) == SSA_NAME)
 	{
 	  if (SSA_NAME_IS_DEFAULT_DEF (arg))
@@ -910,11 +951,7 @@ compute_scalar_jump_functions (struct ip
 
 	      if (index >= 0
 		  && !detect_type_change_ssa (arg, call, jfunc))
-		{
-		  jfunc->type = IPA_JF_PASS_THROUGH;
-		  jfunc->value.pass_through.formal_id = index;
-		  jfunc->value.pass_through.operation = NOP_EXPR;
-		}
+		ipa_set_jf_simple_pass_through (jfunc, index);
 	    }
 	  else
 	    {
@@ -997,9 +1034,7 @@ compute_pass_through_member_ptrs (struct
 		{
 		  struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args,
 								       num);
-		  jfunc->type = IPA_JF_PASS_THROUGH;
-		  jfunc->value.pass_through.formal_id = index;
-		  jfunc->value.pass_through.operation = NOP_EXPR;
+		  ipa_set_jf_simple_pass_through (jfunc, index);
 		}
 	      else
 		undecided_members = true;
@@ -1012,18 +1047,6 @@ compute_pass_through_member_ptrs (struct
   return undecided_members;
 }
 
-/* Simple function filling in a member pointer constant jump function (with PFN
-   and DELTA as the constant value) into JFUNC.  */
-
-static void
-fill_member_ptr_cst_jump_function (struct ipa_jump_func *jfunc,
-				   tree pfn, tree delta)
-{
-  jfunc->type = IPA_JF_CONST_MEMBER_PTR;
-  jfunc->value.member_cst.pfn = pfn;
-  jfunc->value.member_cst.delta = delta;
-}
-
 /* If RHS is an SSA_NAME and it is defined by a simple copy assign statement,
    return the rhs of its defining statement.  */
 
@@ -1091,7 +1114,7 @@ determine_cst_member_ptr (gimple call, t
 	      method = TREE_OPERAND (rhs, 0);
 	      if (delta)
 		{
-		  fill_member_ptr_cst_jump_function (jfunc, rhs, delta);
+		  ipa_set_jf_member_ptr_cst (jfunc, rhs, delta);
 		  return;
 		}
 	    }
@@ -1107,7 +1130,7 @@ determine_cst_member_ptr (gimple call, t
 	      delta = rhs;
 	      if (method)
 		{
-		  fill_member_ptr_cst_jump_function (jfunc, rhs, delta);
+		  ipa_set_jf_member_ptr_cst (jfunc, rhs, delta);
 		  return;
 		}
 	    }
@@ -1681,13 +1704,13 @@ combine_known_type_and_ancestor_jfs (str
   HOST_WIDE_INT combined_offset;
   tree combined_type;
 
-  combined_offset = src->value.known_type.offset + dst->value.ancestor.offset;
-  combined_type = dst->value.ancestor.type;
-
-  dst->type = IPA_JF_KNOWN_TYPE;
-  dst->value.known_type.base_type = src->value.known_type.base_type;
-  dst->value.known_type.offset = combined_offset;
-  dst->value.known_type.component_type = combined_type;
+  combined_offset = ipa_get_jf_known_type_offset (src)
+    + ipa_get_jf_ancestor_offset (dst);
+  combined_type = ipa_get_jf_ancestor_type (dst);
+
+  ipa_set_jf_known_type (dst, combined_offset,
+			 ipa_get_jf_known_type_base_type (src),
+			 combined_type);
 }
 
 /* Update the jump functions associated with call graph edge E when the call
@@ -1804,9 +1827,9 @@ try_make_edge_direct_simple_call (struct
   tree target;
 
   if (jfunc->type == IPA_JF_CONST)
-    target = jfunc->value.constant;
+    target = ipa_get_jf_constant (jfunc);
   else if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
-    target = jfunc->value.member_cst.pfn;
+    target = ipa_get_jf_member_ptr_pfn (jfunc);
   else
     return NULL;
 
@@ -1827,9 +1850,9 @@ try_make_edge_direct_virtual_call (struc
   if (jfunc->type != IPA_JF_KNOWN_TYPE)
     return NULL;
 
-  binfo = TYPE_BINFO (jfunc->value.known_type.base_type);
+  binfo = TYPE_BINFO (ipa_get_jf_known_type_base_type (jfunc));
   gcc_checking_assert (binfo);
-  binfo = get_binfo_at_offset (binfo, jfunc->value.known_type.offset
+  binfo = get_binfo_at_offset (binfo, ipa_get_jf_known_type_offset (jfunc)
 			       + ie->indirect_info->anc_offset,
 			       ie->indirect_info->otr_type);
   if (binfo)
@@ -1881,12 +1904,12 @@ update_indirect_edges_after_inlining (st
 
       jfunc = ipa_get_ith_jump_func (top, ici->param_index);
       if (jfunc->type == IPA_JF_PASS_THROUGH
-	  && jfunc->value.pass_through.operation == NOP_EXPR)
-	ici->param_index = jfunc->value.pass_through.formal_id;
+	  && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
+	ici->param_index = ipa_get_jf_pass_through_formal_id (jfunc);
       else if (jfunc->type == IPA_JF_ANCESTOR)
 	{
- 	  ici->param_index = jfunc->value.ancestor.formal_id;
- 	  ici->anc_offset += jfunc->value.ancestor.offset;
+ 	  ici->param_index = ipa_get_jf_ancestor_formal_id (jfunc);
+ 	  ici->anc_offset += ipa_get_jf_ancestor_offset (jfunc);
 	}
       else
 	/* Either we can find a destination for this edge now or never. */
Index: src/gcc/ipa-prop.h
===================================================================
--- src.orig/gcc/ipa-prop.h
+++ src/gcc/ipa-prop.h
@@ -113,7 +113,7 @@ struct GTY(()) ipa_ancestor_jf_data
 {
   /* Offset of the field representing the ancestor.  */
   HOST_WIDE_INT offset;
-  /* TYpe of the result.  */
+  /* Type of the result.  */
   tree type;
   /* Number of the caller's formal parameter being passed.  */
   int formal_id;
@@ -149,6 +149,108 @@ typedef struct GTY (()) ipa_jump_func
 DEF_VEC_O (ipa_jump_func_t);
 DEF_VEC_ALLOC_O (ipa_jump_func_t, gc);
 
+/* Return the offset of the component that is decribed by a known type jump
+   function JFUNC.  */
+
+static inline HOST_WIDE_INT
+ipa_get_jf_known_type_offset (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_KNOWN_TYPE);
+  return jfunc->value.known_type.offset;
+}
+
+/* Return the base type of a known type jump function JFUNC.  */
+
+static inline tree
+ipa_get_jf_known_type_base_type (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_KNOWN_TYPE);
+  return jfunc->value.known_type.base_type;
+}
+
+/* Return the component type of a known type jump function JFUNC.  */
+
+static inline tree
+ipa_get_jf_known_type_component_type (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_KNOWN_TYPE);
+  return jfunc->value.known_type.component_type;
+}
+
+/* Return the constant stored in a constant jump functin JFUNC.  */
+
+static inline tree
+ipa_get_jf_constant (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_CONST);
+  return jfunc->value.constant;
+}
+
+/* Return the operand of a pass through jmp function JFUNC.  */
+
+static inline tree
+ipa_get_jf_pass_through_operand (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_PASS_THROUGH);
+  return jfunc->value.pass_through.operand;
+}
+
+/* Return the number of the caller's formal parameter that a pass through jump
+   function JFUNC refers to.  */
+
+static inline int
+ipa_get_jf_pass_through_formal_id (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_PASS_THROUGH);
+  return jfunc->value.pass_through.formal_id;
+}
+
+/* Return operation of a pass through jump function JFUNC.  */
+
+static inline enum tree_code
+ipa_get_jf_pass_through_operation (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_PASS_THROUGH);
+  return jfunc->value.pass_through.operation;
+}
+
+/* Return the offset of an ancestor jump function JFUNC.  */
+
+static inline HOST_WIDE_INT
+ipa_get_jf_ancestor_offset (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR);
+  return jfunc->value.ancestor.offset;
+}
+
+/* Return the result type of an ancestor jump function JFUNC.  */
+
+static inline tree
+ipa_get_jf_ancestor_type (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR);
+  return jfunc->value.ancestor.type;
+}
+
+/* Return the number of the caller's formal parameter that an ancestor jump
+   function JFUNC refers to.  */
+
+static inline int
+ipa_get_jf_ancestor_formal_id (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR);
+  return jfunc->value.ancestor.formal_id;
+}
+
+/* Return the pfn part of a member pointer constant jump function JFUNC.  */
+
+static inline tree
+ipa_get_jf_member_ptr_pfn (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_CONST_MEMBER_PTR);
+  return jfunc->value.member_cst.pfn;
+}
+
 /* Summary describing a single formal parameter.  */
 
 struct ipa_param_descriptor

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

* [PATCH 3/5] Use aggregate jump functions in indirect inlining
  2012-06-01  1:24 [PATCH 0/5] Aggregate jump functions Martin Jambor
                   ` (3 preceding siblings ...)
  2012-06-01  1:24 ` [PATCH 1/5] Access methods for " Martin Jambor
@ 2012-06-01  1:25 ` Martin Jambor
  4 siblings, 0 replies; 8+ messages in thread
From: Martin Jambor @ 2012-06-01  1:25 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jan Hubicka

[-- Attachment #1: remove_member_ptr_jfuncs.diff --]
[-- Type: text/plain, Size: 27698 bytes --]

Hi,

this patch uses the aggregate jump functions collected by the previous
one to do indirect inlining.  It can do stuff we could not do before
(see the testcase) and also removes the special constant jump
functions representing C++ function member pointers and represents
that information with ordinary aggregate jump functions and is also
able to indirectly inline through them.

I hoped I'd be able to get rid of type_like_member_ptr_p altogether
but so far I did not and still use it when examining indirect calls
which are from member pointers (we need to match slightly complex
pattern, see ipa_analyze_indirect_call_uses).  The reason is that
there are architectures that store the bit deciding whether the
pointer represents a normal or virtual function in delta, not in the
function pointer field itself and thus I'd need to check values of two
aggregate fields to make sure we do not inline wrong function.  For
now, I needed to moved on and so kept the type check as it is.
It is however ugly and I plan to return to this later.

The patch passed bootstrap and testing on x86_64-linux.

Thanks,

Martin


2012-05-30  Martin Jambor  <mjambor@suse.cz>

	* cgraph.h (cgraph_indirect_call_info): Field anc_offset renamed to
	offset.  New field agg_contents.

	* ipa-prop.h (jump_func_type): Removed IPA_JF_CONST_MEMBER_PTR.
	(ipa_member_ptr_cst): Removed.
	(ipa_jump_func): Removed field member_cst.
	(ipa_find_agg_cst_for_param): Declare.
	(ipa_get_jf_member_ptr_pfn): Removed.

	* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Do not dumo
	const member functions.
	(ipa_set_jf_member_ptr_cst): Removed.
	(type_like_member_ptr_p): Also check field position are known and
	sane.
	(determine_cst_member_ptr): Removed.
	(ipa_compute_jump_functions_for_edge): Remove creation of member
	pointer jump functions.  Simplify.
	(ipa_get_member_ptr_load_param): Incorporate into
	ipa_get_stmt_member_ptr_load_param, also return offset of the required
	field.  Update the remaining caller.
	(ipa_note_param_call): Initialize new fields of
	cgraph_indirect_call_info.
	(ipa_analyze_indirect_call_uses): Also look for simple pointers loaded
	from aggregates.  In such cases, store offset of the called field.
	(ipa_find_agg_cst_for_param): New function.
	(try_make_edge_direct_simple_call): Handle called aggregate values.
	(update_indirect_edges_after_inlining): Update after various
	identifier and parameter changes.
	(ipa_write_jump_function): Do not stream member pointer constant jump
	functions.
	(ipa_read_jump_function): Likewise.
	(ipa_write_indirect_edge_info): Stream new cgraph_indirect_call_info
	fields.
	(ipa_read_indirect_edge_info): Likewise.

	* testsuite/gcc.dg/ipa/iinline-4.c: New test.

Index: src/gcc/cgraph.h
===================================================================
--- src.orig/gcc/cgraph.h
+++ src/gcc/cgraph.h
@@ -338,9 +338,11 @@ typedef enum cgraph_inline_failed_enum {
 
 struct GTY(()) cgraph_indirect_call_info
 {
-  /* Offset accumulated from ancestor jump functions of inlined call graph
-     edges.  */
-  HOST_WIDE_INT anc_offset;
+  /* When polymorphic is set, this field contains offset where the object which
+     was actually used in the polymorphic resides within a larger structure.
+     If agg_contents is set, the field contains the offset within the aggregate
+     from which the address to call was loaded.  */
+  HOST_WIDE_INT offset;
   /* OBJ_TYPE_REF_TOKEN of a polymorphic call (if polymorphic is set).  */
   HOST_WIDE_INT otr_token;
   /* Type of the object from OBJ_TYPE_REF_OBJECT. */
@@ -353,6 +355,9 @@ struct GTY(()) cgraph_indirect_call_info
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
   unsigned polymorphic : 1;
+  /* Set when the call is a call of a pointer loaded from contents of an
+     aggregate at offset.  */
+  unsigned agg_contents : 1;
 };
 
 struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge {
Index: src/gcc/ipa-prop.c
===================================================================
--- src.orig/gcc/ipa-prop.c
+++ src/gcc/ipa-prop.c
@@ -182,14 +182,6 @@ ipa_print_node_jump_functions_for_edge (
 	    }
 	  fprintf (f, "\n");
 	}
-      else if (type == IPA_JF_CONST_MEMBER_PTR)
-	{
-	  fprintf (f, "CONST MEMBER PTR: ");
-	  print_generic_expr (f, jump_func->value.member_cst.pfn, 0);
-	  fprintf (f, ", ");
-	  print_generic_expr (f, jump_func->value.member_cst.delta, 0);
-	  fprintf (f, "\n");
-	}
       else if (type == IPA_JF_PASS_THROUGH)
 	{
 	  fprintf (f, "PASS THROUGH: ");
@@ -356,18 +348,6 @@ ipa_set_ancestor_jf (struct ipa_jump_fun
   jfunc->value.ancestor.agg_preserved = agg_preserved;
 }
 
-/* Simple function filling in a member pointer constant jump function (with PFN
-   and DELTA as the constant value) into JFUNC.  */
-
-static void
-ipa_set_jf_member_ptr_cst (struct ipa_jump_func *jfunc,
-			   tree pfn, tree delta)
-{
-  jfunc->type = IPA_JF_CONST_MEMBER_PTR;
-  jfunc->value.member_cst.pfn = pfn;
-  jfunc->value.member_cst.delta = delta;
-}
-
 /* Structure to be passed in between detect_type_change and
    check_stmt_for_type_change.  */
 
@@ -1013,14 +993,16 @@ type_like_member_ptr_p (tree type, tree
 
   fld = TYPE_FIELDS (type);
   if (!fld || !POINTER_TYPE_P (TREE_TYPE (fld))
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE)
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE
+      || !host_integerp (DECL_FIELD_OFFSET (fld), 1))
     return false;
 
   if (method_ptr)
     *method_ptr = fld;
 
   fld = DECL_CHAIN (fld);
-  if (!fld || INTEGRAL_TYPE_P (fld))
+  if (!fld || INTEGRAL_TYPE_P (fld)
+      || !host_integerp (DECL_FIELD_OFFSET (fld), 1))
     return false;
   if (delta)
     *delta = fld;
@@ -1050,83 +1032,6 @@ get_ssa_def_if_simple_copy (tree rhs)
   return rhs;
 }
 
-/* Traverse statements from CALL backwards, scanning whether the argument ARG
-   which is a member pointer is filled in with constant values.  If it is, fill
-   the jump function JFUNC in appropriately.  METHOD_FIELD and DELTA_FIELD are
-   fields of the record type of the member pointer.  To give an example, we
-   look for a pattern looking like the following:
-
-     D.2515.__pfn ={v} printStuff;
-     D.2515.__delta ={v} 0;
-     i_1 = doprinting (D.2515);  */
-
-static void
-determine_cst_member_ptr (gimple call, tree arg, tree method_field,
-			  tree delta_field, struct ipa_jump_func *jfunc)
-{
-  gimple_stmt_iterator gsi;
-  tree method = NULL_TREE;
-  tree delta = NULL_TREE;
-
-  gsi = gsi_for_stmt (call);
-
-  gsi_prev (&gsi);
-  for (; !gsi_end_p (gsi); gsi_prev (&gsi))
-    {
-      gimple stmt = gsi_stmt (gsi);
-      tree lhs, rhs, fld;
-
-      if (!stmt_may_clobber_ref_p (stmt, arg))
-	continue;
-      if (!gimple_assign_single_p (stmt))
-	return;
-
-      lhs = gimple_assign_lhs (stmt);
-      rhs = gimple_assign_rhs1 (stmt);
-
-      if (TREE_CODE (lhs) != COMPONENT_REF
-	  || TREE_OPERAND (lhs, 0) != arg)
-	return;
-
-      fld = TREE_OPERAND (lhs, 1);
-      if (!method && fld == method_field)
-	{
-	  rhs = get_ssa_def_if_simple_copy (rhs);
-	  if (TREE_CODE (rhs) == ADDR_EXPR
-	      && TREE_CODE (TREE_OPERAND (rhs, 0)) == FUNCTION_DECL
-	      && TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0))) == METHOD_TYPE)
-	    {
-	      method = TREE_OPERAND (rhs, 0);
-	      if (delta)
-		{
-		  ipa_set_jf_member_ptr_cst (jfunc, rhs, delta);
-		  return;
-		}
-	    }
-	  else
-	    return;
-	}
-
-      if (!delta && fld == delta_field)
-	{
-	  rhs = get_ssa_def_if_simple_copy (rhs);
-	  if (TREE_CODE (rhs) == INTEGER_CST)
-	    {
-	      delta = rhs;
-	      if (method)
-		{
-		  ipa_set_jf_member_ptr_cst (jfunc, rhs, delta);
-		  return;
-		}
-	    }
-	  else
-	    return;
-	}
-    }
-
-  return;
-}
-
 /* Helper for determine_known_aggregate_parts, initializes *R for an aggregate
    passed by reference based on BASE and with the given TYPE.  */
 
@@ -1383,32 +1288,20 @@ ipa_compute_jump_functions_for_edge (str
 
       if (is_gimple_ip_invariant (arg))
 	ipa_set_jf_constant (jfunc, arg);
-      else if (!is_gimple_reg_type (TREE_TYPE (arg)))
+      else if (!is_gimple_reg_type (TREE_TYPE (arg))
+	       && TREE_CODE (arg) == PARM_DECL)
 	{
-	  tree method_field, delta_field;
-
-	  /* Aggregate passed by value, check for pass-through, otherwise fill
-	     in aggregate contents.  */
+	  int index = ipa_get_param_decl_index (info, arg);
 
-	  if (TREE_CODE (arg) == PARM_DECL)
+	  gcc_assert (index >=0);
+	  /* Aggregate passed by value, check for pass-through, otherwise we
+	     will attempt to fill in aggregate contents later in this
+	     function.  */
+	  if (is_parm_preserved_before_stmt (&parms_ainfo[index], call, arg))
 	    {
-	      int index = ipa_get_param_decl_index (info, arg);
-	      gcc_assert (index >=0);
-	      if (is_parm_preserved_before_stmt (&parms_ainfo[index], call,
-						 arg))
-		{
-		  ipa_set_jf_simple_pass_through (jfunc, index, true);
-		  continue;
-		}
+	      ipa_set_jf_simple_pass_through (jfunc, index, true);
+	      continue;
 	    }
-
-	  /* TODO: The call to determine_cst_member_ptr will be removed by a
-	     subsequent patch which will do away with IPA_JF_CONST_MEMBER_PTR
-	     altogether.  */
-	  if (type_like_member_ptr_p (TREE_TYPE (arg), &method_field,
-				      &delta_field))
-	    determine_cst_member_ptr (call, arg, method_field, delta_field,
-				      jfunc);
 	}
       else if (TREE_CODE (arg) == SSA_NAME)
 	{
@@ -1442,7 +1335,6 @@ ipa_compute_jump_functions_for_edge (str
 	      || !ipa_get_jf_pass_through_agg_preserved (jfunc))
 	  && (jfunc->type != IPA_JF_ANCESTOR
 	      || !ipa_get_jf_ancestor_agg_preserved (jfunc))
-	  && jfunc->type != IPA_JF_CONST_MEMBER_PTR
 	  && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
 	      || (POINTER_TYPE_P (TREE_TYPE (arg))
 		  && AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (arg))))))
@@ -1474,16 +1366,22 @@ ipa_compute_jump_functions (struct cgrap
     ipa_compute_jump_functions_for_edge (parms_ainfo, cs);
 }
 
-/* If RHS looks like a rhs of a statement loading pfn from a member
-   pointer formal parameter, return the parameter, otherwise return
-   NULL.  If USE_DELTA, then we look for a use of the delta field
-   rather than the pfn.  */
+/* If STMT looks like a statement loading a value from a member pointer formal
+   parameter, return that parameter and store the offset of the field to
+   *OFFSET_P, if it is non-NULL.  Otherwise return NULL (but *OFFSET_P still
+   might be clobbered).  If USE_DELTA, then we look for a use of the delta
+   field rather than the pfn.  */
 
 static tree
-ipa_get_member_ptr_load_param (tree rhs, bool use_delta)
+ipa_get_stmt_member_ptr_load_param (gimple stmt, bool use_delta,
+				    HOST_WIDE_INT *offset_p)
 {
-  tree rec, ref_field, ref_offset, fld, fld_offset, ptr_field, delta_field;
+  tree rhs, rec, ref_field, ref_offset, fld, ptr_field, delta_field;
+
+  if (!gimple_assign_single_p (stmt))
+    return NULL_TREE;
 
+  rhs = gimple_assign_rhs1 (stmt);
   if (TREE_CODE (rhs) == COMPONENT_REF)
     {
       ref_field = TREE_OPERAND (rhs, 1);
@@ -1500,43 +1398,24 @@ ipa_get_member_ptr_load_param (tree rhs,
   if (TREE_CODE (rec) != PARM_DECL
       || !type_like_member_ptr_p (TREE_TYPE (rec), &ptr_field, &delta_field))
     return NULL_TREE;
-
   ref_offset = TREE_OPERAND (rhs, 1);
 
+  if (use_delta)
+    fld = delta_field;
+  else
+    fld = ptr_field;
+  if (offset_p)
+    *offset_p = int_bit_position (fld);
+
   if (ref_field)
     {
       if (integer_nonzerop (ref_offset))
 	return NULL_TREE;
-
-      if (use_delta)
-	fld = delta_field;
-      else
-	fld = ptr_field;
-
       return ref_field == fld ? rec : NULL_TREE;
     }
-
-  if (use_delta)
-    fld_offset = byte_position (delta_field);
   else
-    fld_offset = byte_position (ptr_field);
-
-  return tree_int_cst_equal (ref_offset, fld_offset) ? rec : NULL_TREE;
-}
-
-/* If STMT looks like a statement loading a value from a member pointer formal
-   parameter, this function returns that parameter.  */
-
-static tree
-ipa_get_stmt_member_ptr_load_param (gimple stmt, bool use_delta)
-{
-  tree rhs;
-
-  if (!gimple_assign_single_p (stmt))
-    return NULL_TREE;
-
-  rhs = gimple_assign_rhs1 (stmt);
-  return ipa_get_member_ptr_load_param (rhs, use_delta);
+    return tree_int_cst_equal (byte_position (fld), ref_offset) ? rec
+      : NULL_TREE;
 }
 
 /* Returns true iff T is an SSA_NAME defined by a statement.  */
@@ -1562,8 +1441,9 @@ ipa_note_param_call (struct cgraph_node
 
   cs = cgraph_edge (node, stmt);
   cs->indirect_info->param_index = param_index;
-  cs->indirect_info->anc_offset = 0;
+  cs->indirect_info->offset = 0;
   cs->indirect_info->polymorphic = 0;
+  cs->indirect_info->agg_contents = 0;
   return cs;
 }
 
@@ -1622,7 +1502,9 @@ ipa_note_param_call (struct cgraph_node
 
        return (S.*f)(4);
      }
-*/
+
+   Moreover, the function also looks for called pointers loaded from aggregates
+   passed by value or reference.  */
 
 static void
 ipa_analyze_indirect_call_uses (struct cgraph_node *node,
@@ -1637,6 +1519,7 @@ ipa_analyze_indirect_call_uses (struct c
   gimple branch;
   int index;
   basic_block bb, virt_bb, join;
+  HOST_WIDE_INT offset;
 
   if (SSA_NAME_IS_DEFAULT_DEF (target))
     {
@@ -1647,18 +1530,52 @@ ipa_analyze_indirect_call_uses (struct c
       return;
     }
 
-  /* Now we need to try to match the complex pattern of calling a member
-     pointer. */
+  def = SSA_NAME_DEF_STMT (target);
+  if (gimple_assign_single_p (def))
+    {
+      struct cgraph_edge *cs;
+      HOST_WIDE_INT size, max_size;
+      tree base = get_ref_base_and_extent (gimple_assign_rhs1 (def),
+					   &offset, &size, &max_size);
+      if (max_size == -1 || max_size != size || offset < 0)
+	return;
 
-  if (!POINTER_TYPE_P (TREE_TYPE (target))
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE)
-    return;
+      if (DECL_P (base))
+	{
+	  index = ipa_get_param_decl_index (info, base);
+	  if (index < 0 || !is_parm_preserved_before_stmt (&parms_ainfo[index],
+							   call, base))
+	    return;
+	}
+      else if (TREE_CODE (base) == MEM_REF
+	       && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME
+	       && SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0))
+	       && integer_zerop (TREE_OPERAND (base, 1)))
+	{
 
-  def = SSA_NAME_DEF_STMT (target);
-  if (gimple_code (def) != GIMPLE_PHI)
-    return;
+	  index = ipa_get_param_decl_index (info,
+					    SSA_NAME_VAR (TREE_OPERAND (base,
+									0)));
+	  if (index < 0 || !is_parm_ref_data_preserved (&parms_ainfo[index],
+							call,
+							TREE_OPERAND (base, 0)))
+	    return;
+	}
+      else
+	return;
+
+      cs = ipa_note_param_call (node, index, call);
+      cs->indirect_info->offset = offset;
+      cs->indirect_info->agg_contents = 1;
+      return;
+    }
 
-  if (gimple_phi_num_args (def) != 2)
+  /* Now we need to try to match the complex pattern of calling a member
+     pointer. */
+  if (gimple_code (def) != GIMPLE_PHI
+      || gimple_phi_num_args (def) != 2
+      || !POINTER_TYPE_P (TREE_TYPE (target))
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE)
     return;
 
   /* First, we need to check whether one of these is a load from a member
@@ -1671,15 +1588,15 @@ ipa_analyze_indirect_call_uses (struct c
   d2 = SSA_NAME_DEF_STMT (n2);
 
   join = gimple_bb (def);
-  if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false)))
+  if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false, &offset)))
     {
-      if (ipa_get_stmt_member_ptr_load_param (d2, false))
+      if (ipa_get_stmt_member_ptr_load_param (d2, false, NULL))
 	return;
 
       bb = EDGE_PRED (join, 0)->src;
       virt_bb = gimple_bb (d2);
     }
-  else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false)))
+  else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false, &offset)))
     {
       bb = EDGE_PRED (join, 1)->src;
       virt_bb = gimple_bb (d1);
@@ -1734,15 +1651,19 @@ ipa_analyze_indirect_call_uses (struct c
 
   rec2 = ipa_get_stmt_member_ptr_load_param (def,
 					     (TARGET_PTRMEMFUNC_VBIT_LOCATION
-					      == ptrmemfunc_vbit_in_delta));
-
+					      == ptrmemfunc_vbit_in_delta),
+					     NULL);
   if (rec != rec2)
     return;
 
   index = ipa_get_param_decl_index (info, rec);
   if (index >= 0 && is_parm_preserved_before_stmt (&parms_ainfo[index],
 						   call, rec))
-    ipa_note_param_call (node, index, call);
+    {
+      struct cgraph_edge *cs = ipa_note_param_call (node, index, call);
+      cs->indirect_info->offset = offset;
+      cs->indirect_info->agg_contents = 1;
+    }
 
   return;
 }
@@ -1797,7 +1718,7 @@ ipa_analyze_virtual_call_uses (struct cg
 
   cs = ipa_note_param_call (node, index, call);
   ii = cs->indirect_info;
-  ii->anc_offset = anc_offset;
+  ii->offset = anc_offset;
   ii->otr_token = tree_low_cst (OBJ_TYPE_REF_TOKEN (target), 1);
   ii->otr_type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (target)));
   ii->polymorphic = 1;
@@ -2191,6 +2112,41 @@ ipa_make_edge_direct_to_target (struct c
   return ie;
 }
 
+/* Provided that values from AGG can be propagated to parameter of NODE with
+    index PARAM_INDEX so that is_parm_ref_data_preserved results are reliable
+    and that there is a known constant value in AGG the given OFFSET, return
+    the nown constant value.  */
+
+tree
+ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
+			    HOST_WIDE_INT offset,
+			    struct cgraph_node *node,
+			    int param_index)
+{
+  struct ipa_node_params *info = IPA_NODE_REF (node);
+  tree t = TREE_TYPE (ipa_get_param (info, param_index));
+  struct ipa_agg_jf_item *item;
+  int i;
+
+  if (!agg->items
+      || !ipa_agg_types_propagatable_p (agg, t, 0))
+    return NULL;
+
+  FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, agg->items, i, item)
+    {
+      if (item->offset == offset)
+	{
+	  /* CUrrently we do not have clobber values, return NULL fro them once
+	     we do.  */
+	  gcc_checking_assert (is_gimple_ip_invariant (item->value));
+	  return item->value;
+	}
+      else if (item->offset > offset)
+	return NULL;
+    }
+  return NULL;
+}
+
 /* Try to find a destination for indirect edge IE that corresponds to a simple
    call or a call of a member function pointer and where the destination is a
    pointer formal parameter described by jump function JFUNC.  If it can be
@@ -2198,17 +2154,24 @@ ipa_make_edge_direct_to_target (struct c
 
 static struct cgraph_edge *
 try_make_edge_direct_simple_call (struct cgraph_edge *ie,
-				  struct ipa_jump_func *jfunc)
+				  struct ipa_jump_func *jfunc, int param_index)
 {
   tree target;
 
-  if (jfunc->type == IPA_JF_CONST)
-    target = ipa_get_jf_constant (jfunc);
-  else if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
-    target = ipa_get_jf_member_ptr_pfn (jfunc);
+  if (ie->indirect_info->agg_contents)
+    {
+      target = ipa_find_agg_cst_for_param (&jfunc->agg,
+					   ie->indirect_info->offset,
+					   ie->caller, param_index);
+      if (!target)
+	return NULL;
+    }
   else
-    return NULL;
-
+    {
+      if (jfunc->type != IPA_JF_CONST)
+	return NULL;
+      target = ipa_get_jf_constant (jfunc);
+    }
   return ipa_make_edge_direct_to_target (ie, target);
 }
 
@@ -2229,7 +2192,7 @@ try_make_edge_direct_virtual_call (struc
   binfo = TYPE_BINFO (ipa_get_jf_known_type_base_type (jfunc));
   gcc_checking_assert (binfo);
   binfo = get_binfo_at_offset (binfo, ipa_get_jf_known_type_offset (jfunc)
-			       + ie->indirect_info->anc_offset,
+			       + ie->indirect_info->offset,
 			       ie->indirect_info->otr_type);
   if (binfo)
     target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token,
@@ -2265,6 +2228,7 @@ update_indirect_edges_after_inlining (st
     {
       struct cgraph_indirect_call_info *ici = ie->indirect_info;
       struct ipa_jump_func *jfunc;
+      int param_index;
 
       next_ie = ie->next_callee;
 
@@ -2278,14 +2242,15 @@ update_indirect_edges_after_inlining (st
 	  continue;
 	}
 
-      jfunc = ipa_get_ith_jump_func (top, ici->param_index);
+      param_index = ici->param_index;
+      jfunc = ipa_get_ith_jump_func (top, param_index);
       if (jfunc->type == IPA_JF_PASS_THROUGH
 	  && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
 	ici->param_index = ipa_get_jf_pass_through_formal_id (jfunc);
       else if (jfunc->type == IPA_JF_ANCESTOR)
 	{
  	  ici->param_index = ipa_get_jf_ancestor_formal_id (jfunc);
- 	  ici->anc_offset += ipa_get_jf_ancestor_offset (jfunc);
+ 	  ici->offset += ipa_get_jf_ancestor_offset (jfunc);
 	}
       else
 	/* Either we can find a destination for this edge now or never. */
@@ -2297,7 +2262,8 @@ update_indirect_edges_after_inlining (st
       if (ici->polymorphic)
 	new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc);
       else
-	new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc);
+	new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc,
+							    param_index);
 
       if (new_direct_edge)
 	{
@@ -3207,10 +3173,6 @@ ipa_write_jump_function (struct output_b
       bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1);
       streamer_write_bitpack (&bp);
       break;
-    case IPA_JF_CONST_MEMBER_PTR:
-      stream_write_tree (ob, jump_func->value.member_cst.pfn, true);
-      stream_write_tree (ob, jump_func->value.member_cst.delta, false);
-      break;
     }
 
   count = VEC_length (ipa_agg_jf_item_t, jump_func->agg.items);
@@ -3263,10 +3225,6 @@ ipa_read_jump_function (struct lto_input
       bp = streamer_read_bitpack (ib);
       jump_func->value.ancestor.agg_preserved = bp_unpack_value (&bp, 1);
       break;
-    case IPA_JF_CONST_MEMBER_PTR:
-      jump_func->value.member_cst.pfn = stream_read_tree (ib, data_in);
-      jump_func->value.member_cst.delta = stream_read_tree (ib, data_in);
-      break;
     }
 
   count = streamer_read_uhwi (ib);
@@ -3294,9 +3252,10 @@ ipa_write_indirect_edge_info (struct out
   struct bitpack_d bp;
 
   streamer_write_hwi (ob, ii->param_index);
-  streamer_write_hwi (ob, ii->anc_offset);
+  streamer_write_hwi (ob, ii->offset);
   bp = bitpack_create (ob->main_stream);
   bp_pack_value (&bp, ii->polymorphic, 1);
+  bp_pack_value (&bp, ii->agg_contents, 1);
   streamer_write_bitpack (&bp);
 
   if (ii->polymorphic)
@@ -3318,9 +3277,10 @@ ipa_read_indirect_edge_info (struct lto_
   struct bitpack_d bp;
 
   ii->param_index = (int) streamer_read_hwi (ib);
-  ii->anc_offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
+  ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
   bp = streamer_read_bitpack (ib);
   ii->polymorphic = bp_unpack_value (&bp, 1);
+  ii->agg_contents = bp_unpack_value (&bp, 1);
   if (ii->polymorphic)
     {
       ii->otr_token = (HOST_WIDE_INT) streamer_read_hwi (ib);
Index: src/gcc/ipa-prop.h
===================================================================
--- src.orig/gcc/ipa-prop.h
+++ src/gcc/ipa-prop.h
@@ -44,10 +44,6 @@ along with GCC; see the file COPYING3.
                   argument.
    Unknown      - neither of the above.
 
-   IPA_JF_CONST_MEMBER_PTR stands for C++ member pointers, it is a special
-   constant in this regard because it is in fact a structure consisting of two
-   values.  Other constants are represented with IPA_JF_CONST.
-
    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
@@ -74,7 +70,6 @@ enum jump_func_type
   IPA_JF_UNKNOWN = 0,  /* newly allocated and zeroed jump functions default */
   IPA_JF_KNOWN_TYPE,        /* represented by field known_type */
   IPA_JF_CONST,             /* represented by field costant */
-  IPA_JF_CONST_MEMBER_PTR,  /* represented by field member_cst */
   IPA_JF_PASS_THROUGH,	    /* represented by field pass_through */
   IPA_JF_ANCESTOR	    /* represented by field ancestor */
 };
@@ -128,14 +123,6 @@ struct GTY(()) ipa_ancestor_jf_data
   bool agg_preserved;
 };
 
-/* Structure holding a C++ member pointer constant.  Holds a pointer to the
-   method and delta offset.  */
-struct GTY(()) ipa_member_ptr_cst
-{
-  tree pfn;
-  tree delta;
-};
-
 /* An element in an aggegate part of a jump function describing a known value
    at a given offset.  When the this is part of a pass-through jump function
    with agg_preserved set or an ancestor jump function with agg_preserved set,
@@ -191,7 +178,6 @@ typedef struct GTY (()) ipa_jump_func
   {
     struct ipa_known_type_data GTY ((tag ("IPA_JF_KNOWN_TYPE"))) known_type;
     tree GTY ((tag ("IPA_JF_CONST"))) constant;
-    struct ipa_member_ptr_cst GTY ((tag ("IPA_JF_CONST_MEMBER_PTR"))) member_cst;
     struct ipa_pass_through_data GTY ((tag ("IPA_JF_PASS_THROUGH"))) pass_through;
     struct ipa_ancestor_jf_data GTY ((tag ("IPA_JF_ANCESTOR"))) ancestor;
   } GTY ((desc ("%1.type"))) value;
@@ -311,14 +297,10 @@ ipa_get_jf_ancestor_agg_preserved (struc
   return jfunc->value.ancestor.agg_preserved;
 }
 
-/* Return the pfn part of a member pointer constant jump function JFUNC.  */
+/* Aggregate jump function related functions.  */
+tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *, HOST_WIDE_INT,
+				 struct cgraph_node *, int);
 
-static inline tree
-ipa_get_jf_member_ptr_pfn (struct ipa_jump_func *jfunc)
-{
-  gcc_checking_assert (jfunc->type == IPA_JF_CONST_MEMBER_PTR);
-  return jfunc->value.member_cst.pfn;
-}
 
 /* Summary describing a single formal parameter.  */
 
Index: src/gcc/ipa-cp.c
===================================================================
--- src.orig/gcc/ipa-cp.c
+++ src/gcc/ipa-cp.c
@@ -1110,7 +1110,7 @@ ipa_get_indirect_edge_target (struct cgr
     }
 
   token = ie->indirect_info->otr_token;
-  anc_offset = ie->indirect_info->anc_offset;
+  anc_offset = ie->indirect_info->offset;
   otr_type = ie->indirect_info->otr_type;
 
   t = VEC_index (tree, known_vals, param_index);
Index: src/gcc/testsuite/gcc.dg/ipa/iinline-4.c
===================================================================
--- /dev/null
+++ src/gcc/testsuite/gcc.dg/ipa/iinline-4.c
@@ -0,0 +1,87 @@
+/* Verify that simple indirect calls are inlined even without early
+   inlining..  */
+/* { dg-do compile } */
+/* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining"  } */
+
+struct S
+{
+  int i;
+  void (*f)(struct S *);
+  char c;
+};
+
+extern void non_existent(struct S *p, int);
+
+static void hooray1 (struct S *p)
+{
+  non_existent (p, 1);
+}
+
+static void hiphip1 (struct S *p)
+{
+  p->f (p);
+}
+
+int test1 (void)
+{
+  struct S s;
+  s.i = 1234;
+  s.f = hooray1;
+  s.c = 'c';
+  hiphip1 (&s);
+  return 0;
+}
+
+struct S gs;
+struct S *gp = &gs;
+
+static void hooray2 (struct S *p)
+{
+  non_existent (p, 2);
+}
+
+static void hip2 (struct S *p)
+{
+  p->f (p);
+}
+
+static void hiphip2 (struct S *p)
+{
+  hip2 (p);
+}
+
+int test2 (void)
+{
+  struct S *p = gp;
+  p->i = 2341;
+  p->f = hooray2;
+  p->c = 'c';
+  hiphip2 (p);
+  return 0;
+}
+
+static void hooray3 (struct S *p)
+{
+  non_existent (p, 3);
+}
+
+static void hiphip3 (struct S s)
+{
+  s.f (&s);
+}
+
+int test3(void)
+{
+  struct S s;
+  s.i = 3412;
+  s.f = hooray3;
+  s.c = 'c';
+  hiphip3 (s);
+  return 0;
+}
+
+
+/* { dg-final { scan-ipa-dump "hooray1\[^\\n\]*inline copy in test1"  "inline"  } } */
+/* { dg-final { scan-ipa-dump "hooray2\[^\\n\]*inline copy in test2"  "inline"  } } */
+/* { dg-final { scan-ipa-dump "hooray2\[^\\n\]*inline copy in test2"  "inline"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */

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

* Re: [PATCH 1/5] Access methods for jump functions
  2012-06-01  1:24 ` [PATCH 1/5] Access methods for " Martin Jambor
@ 2012-06-01  8:49   ` Jan Hubicka
  0 siblings, 0 replies; 8+ messages in thread
From: Jan Hubicka @ 2012-06-01  8:49 UTC (permalink / raw)
  To: Martin Jambor; +Cc: GCC Patches, Jan Hubicka

> Hi,
> 
> the first patch mainly introduces access methods to read parts of jump
> functions.  I am no particular fan of these but the are now more
> widely spread than originally and above all checking asserts verifying
> that the correct part of the union is read should really be useful.
> 
> I have also unified creation of jump function in ipa-prop.c to new
> functions so that all fields are initialized.  On the contrary, I have
> left update_jump_functions_after_inlining largely alone because it
> modifies jump functions in place (and there may be good reason not to
> touch some parts in the future, if not now), dumping functions and
> streaming because they are a bit special and simple too.
> 
> There is also a tiny amount of other cleanup.
> 
> Bootstrapped separately on x86_64-linux.  OK for trunk?
> 
> Thanks,
> 
> Martin
> 
> 
> 
> 2012-05-03  Martin Jambor  <mjambor@suse.cz>
> 
> 	* ipa-prop.h (ipa_get_jf_known_type_offset): New function.
> 	(ipa_get_jf_known_type_base_type): Likewise.
> 	(ipa_get_jf_known_type_component_type): Likewise.
> 	(ipa_get_jf_constant): Likewise.
> 	(ipa_get_jf_pass_through_formal_id): Likewise.
> 	(ipa_get_jf_pass_through_operation): Likewise.
> 	(ipa_get_jf_ancestor_offset): Likewise.
> 	(ipa_get_jf_ancestor_type): Likewise.
> 	(ipa_get_jf_ancestor_formal_id): Likewise.
> 	(ipa_get_jf_member_ptr_pfn): Likewise.
> 
> 	* ipa-prop.c (ipa_set_jf_known_type): New function.
> 	(ipa_set_jf_constant): Likewise.
> 	(ipa_set_jf_simple_pass_through): Likewise.
> 	(ipa_set_jf_arith_pass_through): Likewise.
> 	(ipa_set_ancestor_jf): Likewise.
> 	(fill_member_ptr_cst_jump_function): Moved up and renamed to
> 	ipa_set_jf_member_ptr_cst.
> 	(detect_type_change_1): Use the new jump function creation functions.
> 	(compute_complex_assign_jump_func): Likewise.
> 	(compute_complex_ancestor_jump_func): Likewise.
> 	(compute_known_type_jump_func): Likewise.
> 	(compute_scalar_jump_functions): Likewise.
> 	(compute_pass_through_member_ptrs): Likewise.
> 	(determine_cst_member_ptr): Likewise.
> 	(combine_known_type_and_ancestor_jfs): Likewise.
> 	(try_make_edge_direct_simple_call): Likewise.
> 	(try_make_edge_direct_virtual_call): Likewise.
> 	(update_indirect_edges_after_inlining): Likewise.
> 
> 	* ipa-cp.c (ipa_get_jf_pass_through_result): Use jump function
> 	access functions.  Incorporat NOP_EXPR and BINFO handling from its
> 	callers.
> 	(ipa_get_jf_ancestor_result): Likewise.  Incorporate handling BINFOs
> 	which was in its callers.
> 	(ipa_value_from_jfunc): Use jump function access functions.  Some
> 	functionality moved to functions above.
> 	(propagate_vals_accross_ancestor): Likewise.
> 	(propagate_vals_accross_pass_through): Use jump function access
> 	functions.
> 	(propagate_accross_jump_function): Likewise.
> 
> 	* ipa-inline-analysis.c (remap_edge_change_prob): Use jump function
> 	access functions.
> 	(inline_merge_summary): Likewise.

OK,
thanks!
Honza

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

* Re: [PATCH 2/5] Build aggregate jump functions
  2012-06-01  1:24 ` [PATCH 2/5] Build aggregate jump functions Martin Jambor
@ 2012-06-01 11:33   ` Richard Guenther
  0 siblings, 0 replies; 8+ messages in thread
From: Richard Guenther @ 2012-06-01 11:33 UTC (permalink / raw)
  To: Martin Jambor; +Cc: GCC Patches, Jan Hubicka

On Fri, Jun 1, 2012 at 2:02 AM, Martin Jambor <mjambor@suse.cz> wrote:
> Hi,
>
> this patch adds the capability to build aggregate jump functions.
> These consist of:
>
> 1) information what known compile time IPA-invariants are at various
>   offsets of an aggregate passed to a callee either by reference
>   (when the parameter is pointer) or by value (when it is an
>   aggregate).  This patch simply scans the current BB backwards from
>   the call statement and examines what values are stored there.  Even
>   this simple approach does not look particularly simple
>   (determine_known_aggregate_parts), it is however usually sufficient
>   for Fortran array descriptions (in the testcases I have seen).  For
>   more advanced uses like devirtualization we'll need something that
>   will examine the whole function but will look quite like this, only
>   even more messy, I'm afraid.
>
>   When we do this, we also record the type through which data was
>   stored into the aggregate, which is either the type of a DECL or
>   type stored in the offset of a  MEM_REF.
>
> 2) Being able to conservatively but usefully recognize that an
>   aggregate (passed either by reference or a value) that we got from
>   a caller and pass it to a callee has not changed.
>
>   This is slightly complex in cases where aggregates are passed by
>   reference.  Because in gimple pointer types carry more-or-less no
>   information about the data it points to, we'd normally have to feed
>   AA with a type that aliases all in order to get conservatively
>   correct results.  That might however be too much conservative.  We
>   circumvent this problem by feeding AA the pointed-to type but also
>   verifying that the aggregate data was stored with the same type (or
>   a type containing the pointed-to type at the expected offset).
>
>   The data structures can also represent such pass-through functions
>   with known listed exceptions but that is not currently implemented
>   and is left for later.
>
> However, this patch does not use the collected data in any way, that
> is what two subsequent patches do.
>
> The patch passes bootstrap and testing on x86_64-linux.

Quite a large patch ... let's iterate a bit about the aliasing stuff.
If I understand
correctly you have

... foo (struct X *p)
{
  .... (1)
  bar (p);
}

and want to know whether *p was modified before the call to bar so you
can pass thru the jump function (you are not interested to modify the jump
function, clearing clobbered information yet?).

Now I don't understand on how you might be able to ever use type-based
alias info to disambiguate any may-defs of *p.  Can you explain why
you believe you can do so?

I understand you possibly want to verify whether a consumer of *p
can eventually be replaced by the jump function value, thus the value
is of matching type, but would that be not only applicable at transform time?
Given that you'd always be able to fallback to

 'const' typeof (*p) tem = <jump function>;
 ... = properly-typed-read-from-tem-with-alias-set-zero;

and leave constant propagation to the optimizers?

At least I think you confuse what "type" you can use at value-replacement
time with the "type" you use for determining whether *p is invalidated.


>  static void
> -compute_cst_member_ptr_arguments (struct ipa_edge_args *args,
> -                                 gimple call)
> +init_ao_ref_for_byref_agg_jf (ao_ref *r, tree base, tree type)
> +{
> +  static tree alias_all_type = NULL;
> +
> +  if (!alias_all_type)
> +    alias_all_type = build_pointer_type_for_mode (char_type_node,
> +                                                 ptr_mode, true);
> +  ao_ref_init (r, build2 (MEM_REF, type, base,
> +                         build_int_cst (alias_all_type, 0)));
> +}

this won't survive garbage collection I believe.  I think you can more easily
use ao_ref_init_from_ptr_and_size here:

  ao_ref_init_from_ptr_and_size (r, base, TYPE_SIZE_UNIT (TREE_TYPE (base)));

that makes sure to use alias-set zero.

As you've seen in the recent bugreport the alias stmt walking can be quite
a compile-time hog so you should eventually limit yourself conservatively
by using the number of stmts walked returned by walk_aliased_vdefs.

Richard.

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

end of thread, other threads:[~2012-06-01 11:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-06-01  1:24 [PATCH 0/5] Aggregate jump functions Martin Jambor
2012-06-01  1:24 ` [PATCH 5/5] Compute predicates for phi node results in ipa-inline-analysis.c Martin Jambor
2012-06-01  1:24 ` [PATCH 4/5] Incorporate aggregate jump functions to inlining analysis Martin Jambor
2012-06-01  1:24 ` [PATCH 2/5] Build aggregate jump functions Martin Jambor
2012-06-01 11:33   ` Richard Guenther
2012-06-01  1:24 ` [PATCH 1/5] Access methods for " Martin Jambor
2012-06-01  8:49   ` Jan Hubicka
2012-06-01  1:25 ` [PATCH 3/5] Use aggregate jump functions in indirect inlining Martin Jambor

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).