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  <mjam...@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

Reply via email to