This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [1/3] Build aggregate jump functions


On Thu, 2 Aug 2012, Martin Jambor wrote:

> Hi,
> 
> this is the third version of aggregate jump function patch.  There are
> three major differences from the previous one:
> 
>   1. Because using TBAA when computing pass-through jump functions made
>      difference only in less than 2% of those and required complex
>      type checking when doing IPA propagation, I removed it.
> 
>   2. In order to create aggregate jump functions even in cases like
>      the PR 53787 testcase, I also compute aggregate jump functions
>      for pointers to non-aggregates.  This makes the name aggregate
>      jump function a bit misleading but I could not think of a better
>      one :-)
> 
>   3. The previous two changes require that in each aggregate jump
>      function I store whether it describes values passed by reference
>      or by value.  I added an LTO testcase which breaks if I don't
>      (gcc.dg/lto/20120723_[01].c) but it is invalid C because the
>      prototypes of corresponding functions in each file do not match.
>      However, current trunk does not "miscompile" it and I'm afraid
>      code like this can be found in thee wild so I do not want to
>      either.  On the other hand, I'm not sure whether we want an
>      essentially invalid testcase in the testsuite, should I remove
>      it?

Just keep it.

> In addition to that, a small improvement of ipa_load_from_parm_agg_1
> allows us to catch cases when pointers to aggregates are not gimple
> registers (as e.g. in hiphip7 and hip7 in gcc.dg/ipa/iinline-4.c).
> 
> Alias analysis is used in three places when doing intra-procedural
> analysis:
> 
>   1. In determine_known_aggregate_parts where we look at what data are
>      passed to a callee by value in an aggregate actual argument or by
>      reference through a pointer.  It just scans a BB back from the
>      call statement, uses stmt_may_clobber_ref_p_1 to figure out
>      whether a statement can clobber the passed value, building the
>      jump function along the way and stopping at the first potential
>      clobber that it cannot build a jump function from or safely
>      ignore.  I use ao_ref_init and thus TBAA for stuff passed by
>      value (because I'm looking at a load in the call statement) and
>      ao_ref_init_from_ptr_and_size and therefore no TBAA for pointers
>      (because we are not).
> 
>   2. In parm_ref_data_pass_through_p where we try to figure out
>      whether passing a pointer from a formal argument to an actual
>      argument of a callee also passes the data it points to or whether
>      that data might be clobbered. ao_ref_init_from_ptr_and_size and
>      thus no TBAA is used.
> 
>      In order to cap complexity, if the referred data by a pointer is
>      detected to be clobbered at one call site, it is subsequently
>      considered clobbered at all investigated call sites.  We also
>      keep a bitmap of vdefs visited by walk_aliased_vdefs.  The worst
>      case complexity therefore is that we walk each vdef for each
>      formal parameter of the current function.
> 
>   3. In ipa_load_from_parm_agg_1, where we are looking at a load and
>      need to figure out whether we could later on replace that load
>      from a value from a jump function (if we are lucky at IPA stage).
>      Thus it needs to find out whether it is a load from a formal
>      parameter or data pointed to by a formal parameter and whether
>      that data might have been clobbered since function started
>      executing.  Because we always have a load at hand, we use
>      ao_ref_init in parm_ref_data_preserved_p and
>      parm_preserved_before_stmt_p to initialize the ao_ref.
> 
>      In order not to do too much vdef walking, once we determine that
>      data is clobbered, all such subsequent checks do not invoke AA
>      and pessimistically consider the data clobbered too.  Because the
>      references may be very different, we cannot cache visited
>      statements in a bitmap and thus in the the theoretical worst case
>      parm_ref_data_preserved_p can always walk all vdefs in the
>      current function.  (Eventually, parm_ref_data_preserved_p1 will
>      be called for all loads through parameters, in this patch it is
>      called only once per an indirect call.)  On the other hand, I
>      assume that big functions generally clobber memory and we would
>      thus soon fall back on returning false without performing any AA.
> 
>   4. In parm_preserved_before_stmt_p is used whenever we want to find
>      out whether a load from a non-gimple-register PARM_DECL loads
>      data that has not been modified in this function.
> 
>      It is already in trunk, though its name is now
>      is_parm_modified_before_stmt and it is used for address taken
>      scalar parameters.  I have renamed the function for the sake of
>      consistency and added two new uses: When we pass an aggregate
>      PARM_DECL to a callee and want to determine whether it is
>      unchanged, and ipa_load_from_parm_agg_1 (described above) when it
>      looks at a load from an aggregate PARM_DECL.  In all cases it
>      looks at a load, no pointers are involved and so I use
>      ao_ref_init and its TBAA.
> 
>      This function too falls back to "return false for this PARM_DECL"
>      mode once the decl is considered clobbered.  For naked PARM_DECL
>      loads it also caches visited statements, it cannot do that for
>      other references.  This bitmap is different from the one from
>      point 2, function hiphip7 from gcc.dg/ipa/iinline-4.c is an
>      example where both are used together but differently to construct
>      an aggregate pass-through function (for data pointed to by an
>      address taken parameter).
> 
> I'm currently looking into how to best use the return value of
> walk_aliased_vdefs in order not to  cause grave compile-time issues
> even in the unlikely cases.
> 
> The other half of the patch is manipulation with IPA structures,
> mainly ipa_jump_func but also cgraph_indirect_call_info.  I'll be
> happy to explain any of it in detail too but I guess this email is
> already too long and I should get the functions above (and AA usage in
> general) approved first and then ask Honza to look at the rest.
> 
> The patch of course passes bootstrap and testing on x86_64-linux.  I
> also LTO-built Firefox with its slightly older version which behaved
> just as what I got when I non-LTO-built it with pristine trunk
> (i.e. it crashed on an assert after a while, but that is probably a
> problem of the particular Firefox checkout, I don't know yet).
> 
> Thanks for any comments,

The patch looks good to me.  Thus it is ok for trunk.

Thanks,
Richard.

> Martin
> 
> 
> 
> 2012-07-30  Martin Jambor  <mjambor@suse.cz>
> 
> 	* cgraph.h (cgraph_indirect_call_info): Field anc_offse renamd to
> 	offset, updated all users.  New field agg_contents.
> 
> 	* ipa-prop.h (jump_func_type): Removed IPA_JF_CONST_MEMBER_PTR.
> 	(ipa_pass_through_data): New field agg_preserved.
> 	(ipa_ancestor_jf_data): Likewise.
> 	(ipa_member_ptr_cst): Removed.
> 	(ipa_agg_jf_item): New type.
> 	(ipa_agg_jump_function): Likewise.
> 	(ipa_jump_func): New field agg.  Removed field member_cst.
> 	(ipa_get_jf_pass_through_agg_preserved): New function.
> 	(ipa_get_jf_ancestor_agg_preserved): Likewise.
> 	(ipa_get_jf_member_ptr_pfn): Removed.
> 	(ipa_find_agg_cst_for_param): Declare.
> 	(ipa_load_from_parm_agg): Likewise.
> 
> 	* ipa-prop.c (param_analysis_info): Fields modified and
> 	visited_statements rename to parm_modified and parm_visited_statements
> 	respectively, added fields ref_modified, ref_visited_statements,
> 	pt_modified and pt_visited_statements.
> 	(ipa_print_node_jump_functions_for_edge): Do not dump const member
> 	functions.  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.
> 	(ipa_set_jf_member_ptr_cst): Removed.
> 	(is_parm_modified_before_stmt): Logic reversed, renamed to
> 	parm_preserved_before_stmt_p.  Cache visited bitmap only for
> 	naked DECL parameters.  All callers updated.
> 	(load_from_unmodified_param): Allow NULL parms_ainfo.
> 	(parm_ref_data_preserved_p): New function.
> 	(parm_ref_data_pass_through_p): Likewise.
> 	(ipa_load_from_parm_agg_1): Likewise.
> 	(ipa_load_from_parm_agg): Likewise.
> 	(compute_complex_assign_jump_func): Check if aggregate contents are
> 	preserved.
> 	(compute_complex_ancestor_jump_func): Likewise.
> 	(compute_scalar_jump_functions): Removed.
> 	(type_like_member_ptr_p): Also check field position are known and
> 	sane.
> 	(compute_pass_through_member_ptrs): Removed.
> 	(determine_cst_member_ptr): Likewise.
> 	(ipa_known_agg_contents_list): New type.
> 	(determine_known_aggregate_parts): New function.
> 	(compute_cst_member_ptr_arguments): Removed.
> 	(ipa_compute_jump_functions_for_edge): Compute all kinds of jump
> 	functions (scalar, aggregate and member pointer).
> 	(ipa_get_member_ptr_load_param): Incorporate into
> 	ipa_get_stmt_member_ptr_load_param, also pass back an offset.
> 	(ipa_note_param_call): Clear agg_contents.
> 	(ipa_analyze_indirect_call_uses): Also look for simple pointers loaded
> 	from aggregates.  In such cases, store offset of the called field.
> 	(ipa_analyze_node): Initialize new fields of param_analysis_info.
> 	(update_jump_functions_after_inlining): Handle aggregate contents.
> 	(ipa_find_agg_cst_for_param): New function.
> 	(try_make_edge_direct_simple_call): Handle called aggregate values.
> 	(update_indirect_edges_after_inlining): Make sure aggregate preserving
> 	jump functions comply with type compatibility requirements.
> 	(ipa_edge_duplication_hook): Copy also aggregate jump functions.
> 	(ipa_write_jump_function): Stream agg_preserved flags and aggregate
> 	jump functions.  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.
> 	* testsuite/gcc.dg/ipa/iinline-5.c: Likewise.
> 	* testsuite/gcc.dg/ipa/iinline-6.c: Likewise.
> 	* testsuite/gcc.dg/ipa/iinline-7.c: Likewise.
> 	* testsuite/gcc.dg/lto/20120723_0.c: Likewise.
> 	* testsuite/gcc.dg/lto/20120723_1.c: Likewise.
> 
> Index: src/gcc/ipa-prop.h
> ===================================================================
> *** src.orig/gcc/ipa-prop.h
> --- src/gcc/ipa-prop.h
> *************** along with GCC; see the file COPYING3.
> *** 44,53 ****
>                     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
> --- 44,49 ----
> *************** enum jump_func_type
> *** 74,80 ****
>     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 */
>   };
> --- 70,75 ----
> *************** struct GTY(()) ipa_pass_through_data
> *** 104,109 ****
> --- 99,111 ----
>        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, it is set to true only when we are
> +      certain that no write to the object it points to 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 the operation is
> +      NOP_EXPR.  */
> +   bool agg_preserved;
>   };
>   
>   /* Structure holding data required to describe an ancestor pass-through
> *************** struct GTY(()) ipa_ancestor_jf_data
> *** 117,137 ****
>     tree type;
>     /* Number of the caller's formal parameter being passed.  */
>     int formal_id;
>   };
>   
> ! /* 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;
>   };
>   
>   /* 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
>   {
>     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
> --- 119,174 ----
>     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;
>   };
>   
> ! /* An element in an aggegate part of a jump function describing a known value
> !    at a given offset.  When it is part of a pass-through jump function with
> !    agg_preserved set or an ancestor jump function with agg_preserved set, all
> !    unlisted positions are assumed to be preserved but the value can be a type
> !    node, which means that the particular piece (starting at offset and having
> !    the size of the type) is clobbered with an unknown value.  When
> !    agg_preserved is false or the type of the containing jump function is
> !    different, all unlisted parts are assumed to be unknown and all values must
> !    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;
> !   /* True if the data was passed by reference (as opposed to by value). */
> !   bool by_ref;
>   };
>   
> + typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
> + 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
> *************** typedef struct GTY (()) ipa_jump_func
> *** 140,146 ****
>     {
>       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;
> --- 177,182 ----
> *************** ipa_get_jf_pass_through_operation (struc
> *** 214,219 ****
> --- 250,264 ----
>     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
> *************** ipa_get_jf_ancestor_formal_id (struct ip
> *** 242,254 ****
>     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.  */
> --- 287,299 ----
>     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;
>   }
>   
>   /* Summary describing a single formal parameter.  */
> *************** struct cgraph_edge *ipa_make_edge_direct
> *** 456,461 ****
> --- 501,512 ----
>   /* Functions related to both.  */
>   void ipa_analyze_node (struct cgraph_node *);
>   
> + /* Aggregate jump function related functions.  */
> + tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *, HOST_WIDE_INT,
> + 				 bool);
> + bool ipa_load_from_parm_agg (struct ipa_node_params *, gimple, tree, int *,
> + 			     HOST_WIDE_INT *, bool *);
> + 
>   /* Debugging interface.  */
>   void ipa_print_node_params (FILE *, struct cgraph_node *node);
>   void ipa_print_all_params (FILE *);
> Index: src/gcc/ipa-prop.c
> ===================================================================
> *** src.orig/gcc/ipa-prop.c
> --- src/gcc/ipa-prop.c
> *************** along with GCC; see the file COPYING3.
> *** 44,51 ****
>   
>   struct param_analysis_info
>   {
> !   bool modified;
> !   bitmap visited_statements;
>   };
>   
>   /* Vector where the parameter infos are actually stored. */
> --- 44,51 ----
>   
>   struct param_analysis_info
>   {
> !   bool parm_modified, ref_modified, pt_modified;
> !   bitmap parm_visited_statements, pt_visited_statements;
>   };
>   
>   /* Vector where the parameter infos are actually stored. */
> *************** ipa_print_node_jump_functions_for_edge (
> *** 178,201 ****
>   	    }
>   	  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: ");
> ! 	  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, "\n");
>   	}
>         else if (type == IPA_JF_ANCESTOR)
> --- 178,198 ----
>   	    }
>   	  fprintf (f, "\n");
>   	}
>         else if (type == IPA_JF_PASS_THROUGH)
>   	{
>   	  fprintf (f, "PASS THROUGH: ");
> ! 	  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)
> ! 	    {
> ! 	      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)
> *************** ipa_print_node_jump_functions_for_edge (
> *** 205,212 ****
> --- 202,235 ----
>   		   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, "         Aggregate passed by %s:\n",
> + 		   jump_func->agg.by_ref ? "reference" : "value");
> + 	  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");
> + 	    }
> + 	}
>       }
>   }
>   
> *************** ipa_set_jf_constant (struct ipa_jump_fun
> *** 286,297 ****
>   
>   /* 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.  */
> --- 309,322 ----
>   
>   /* 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,
> ! 				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.  */
> *************** ipa_set_jf_arith_pass_through (struct ip
> *** 304,333 ****
>     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
> --- 329,348 ----
>     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, 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;
>   }
>   
>   /* Structure to be passed in between detect_type_change and
> *************** mark_modified (ao_ref *ao ATTRIBUTE_UNUS
> *** 581,610 ****
>     return true;
>   }
>   
> ! /* Return true if the formal parameter PARM might have been 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)
>   {
>     bool modified = false;
>     ao_ref refd;
>   
> !   if (parm_ainfo->modified)
> !     return true;
>   
>     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);
> !   if (modified)
> !     {
> !       parm_ainfo->modified = true;
> !       return true;
> !     }
> !   return false;
>   }
>   
>   /* If STMT is an assignment that loads a value from an parameter declaration,
> --- 596,630 ----
>     return true;
>   }
>   
> ! /* Return true if a load from a formal parameter PARM_LOAD is known to retreive
> !    a value 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 the parameter.  */
>   
>   static bool
> ! parm_preserved_before_stmt_p (struct param_analysis_info *parm_ainfo,
> ! 			       gimple stmt, tree parm_load)
>   {
>     bool modified = false;
> +   bitmap *visited_stmts;
>     ao_ref refd;
>   
> !   if (parm_ainfo && parm_ainfo->parm_modified)
> !     return false;
>   
>     gcc_checking_assert (gimple_vuse (stmt) != NULL_TREE);
> !   ao_ref_init (&refd, parm_load);
> !   /* We can cache visited statements only when parm_ainfo is available and when
> !      we are looking at a naked load of the whole parameter.  */
> !   if (!parm_ainfo || TREE_CODE (parm_load) != PARM_DECL)
> !     visited_stmts = NULL;
> !   else
> !     visited_stmts = &parm_ainfo->parm_visited_statements;
> !   walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified, &modified,
> ! 		      visited_stmts);
> !   if (parm_ainfo && modified)
> !     parm_ainfo->parm_modified = true;
> !   return !modified;
>   }
>   
>   /* If STMT is an assignment that loads a value from an parameter declaration,
> *************** load_from_unmodified_param (struct ipa_n
> *** 628,639 ****
>   
>     index = ipa_get_param_decl_index (info, op1);
>     if (index < 0
> !       || is_parm_modified_before_stmt (&parms_ainfo[index], stmt, op1))
>       return -1;
>   
>     return index;
>   }
>   
>   /* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
>      of an assignment statement STMT, try to determine whether we are actually
>      handling any of the following cases and construct an appropriate jump
> --- 648,805 ----
>   
>     index = ipa_get_param_decl_index (info, op1);
>     if (index < 0
> !       || !parm_preserved_before_stmt_p (parms_ainfo ? &parms_ainfo[index]
> ! 					: NULL, stmt, op1))
>       return -1;
>   
>     return index;
>   }
>   
> + /* Return true if memory reference REF loads data that are known to be
> +    unmodified in this function before reaching statement STMT.  PARM_AINFO, if
> +    non-NULL, is a pointer to a structure containing temporary information about
> +    PARM.  */
> + 
> + static bool
> + parm_ref_data_preserved_p (struct param_analysis_info *parm_ainfo,
> + 			      gimple stmt, tree ref)
> + {
> +   bool modified = false;
> +   ao_ref refd;
> + 
> +   gcc_checking_assert (gimple_vuse (stmt));
> +   if (parm_ainfo && parm_ainfo->ref_modified)
> +     return false;
> + 
> +   ao_ref_init (&refd, ref);
> +   walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified, &modified,
> + 		      NULL);
> +   if (parm_ainfo && modified)
> +     parm_ainfo->ref_modified = true;
> +   return !modified;
> + }
> + 
> + /* Return true if the data pointed to by PARM is known to be unmodified in this
> +    function before reaching call statement CALL into which it is passed.
> +    PARM_AINFO is a pointer to a structure containing temporary information
> +    about PARM.  */
> + 
> + static bool
> + parm_ref_data_pass_through_p (struct param_analysis_info *parm_ainfo,
> + 			       gimple call, tree parm)
> + {
> +   bool modified = false;
> +   ao_ref refd;
> + 
> +   /* It's unnecessary to calculate anything about memory contnets for a const
> +      function because it is not goin to use it.  But do not cache the result
> +      either.  Also, no such calculations for non-pointers.  */
> +   if (!gimple_vuse (call)
> +       || !POINTER_TYPE_P (TREE_TYPE (parm)))
> +     return false;
> + 
> +   if (parm_ainfo->pt_modified)
> +     return false;
> + 
> +   ao_ref_init_from_ptr_and_size (&refd, parm, NULL_TREE);
> +   walk_aliased_vdefs (&refd, gimple_vuse (call), mark_modified, &modified,
> + 		      parm_ainfo ? &parm_ainfo->pt_visited_statements : NULL);
> +   if (modified)
> +     parm_ainfo->pt_modified = true;
> +   return !modified;
> + }
> + 
> + /* Return true if we can prove that OP is a memory reference loading unmodified
> +    data from an aggregate passed as a parameter and if the aggregate is passed
> +    by reference, that the alias type of the load corresponds to the type of the
> +    formal parameter (so that we can rely on this type for TBAA in callers).
> +    INFO and PARMS_AINFO describe parameters of the current function (but the
> +    latter can be NULL), STMT is the load statement.  If function returns true,
> +    *INDEX_P, *OFFSET_P and *BY_REF is filled with the parameter index, offset
> +    within the aggregate and whether it is a load from a value passed by
> +    reference respectively.  */
> + 
> + static bool
> + ipa_load_from_parm_agg_1 (struct ipa_node_params *info,
> + 			  struct param_analysis_info *parms_ainfo, gimple stmt,
> + 			  tree op, int *index_p, HOST_WIDE_INT *offset_p,
> + 			  bool *by_ref_p)
> + {
> +   int index;
> +   HOST_WIDE_INT size, max_size;
> +   tree 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 (DECL_P (base))
> +     {
> +       int index = ipa_get_param_decl_index (info, base);
> +       if (index >= 0
> + 	  && parm_preserved_before_stmt_p (parms_ainfo ? &parms_ainfo[index]
> + 					   : NULL, stmt, op))
> + 	{
> + 	  *index_p = index;
> + 	  *by_ref_p = false;
> + 	  return true;
> + 	}
> +       return false;
> +     }
> + 
> +   if (TREE_CODE (base) != MEM_REF
> + 	   || TREE_CODE (TREE_OPERAND (base, 0)) != SSA_NAME
> + 	   || !integer_zerop (TREE_OPERAND (base, 1)))
> +     return false;
> + 
> +   if (SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0)))
> +     {
> +       tree parm = SSA_NAME_VAR (TREE_OPERAND (base, 0));
> +       index = ipa_get_param_decl_index (info, parm);
> +     }
> +   else
> +     {
> +       /* This branch catches situations where a pointer parameter is not a
> + 	 gimple register, for example:
> + 
> + 	 void hip7(S*) (struct S * p)
> + 	 {
> + 	 void (*<T2e4>) (struct S *) D.1867;
> + 	 struct S * p.1;
> + 
> + 	 <bb 2>:
> + 	 p.1_1 = p;
> + 	 D.1867_2 = p.1_1->f;
> + 	 D.1867_2 ();
> + 	 gdp = &p;
> +       */
> + 
> +       gimple def = SSA_NAME_DEF_STMT (TREE_OPERAND (base, 0));
> +       index = load_from_unmodified_param (info, parms_ainfo, def);
> +     }
> + 
> +   if (index >= 0
> +       && parm_ref_data_preserved_p (parms_ainfo ? &parms_ainfo[index] : NULL,
> + 				    stmt, op))
> +     {
> +       *index_p = index;
> +       *by_ref_p = true;
> +       return true;
> +     }
> +   return false;
> + }
> + 
> + /* Just like the previous function, just without the param_analysis_info
> +    pointer, for users outside of this file.  */
> + 
> + bool
> + ipa_load_from_parm_agg (struct ipa_node_params *info, gimple stmt,
> + 			tree op, int *index_p, HOST_WIDE_INT *offset_p,
> + 			bool *by_ref_p)
> + {
> +   return ipa_load_from_parm_agg_1 (info, NULL, stmt, op, index_p, offset_p,
> + 				   by_ref_p);
> + }
> + 
>   /* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
>      of an assignment statement STMT, try to determine whether we are actually
>      handling any of the following cases and construct an appropriate jump
> *************** compute_complex_assign_jump_func (struct
> *** 731,737 ****
>   	}
>         else if (gimple_assign_single_p (stmt)
>   	       && !detect_type_change_ssa (tc_ssa, call, jfunc))
> ! 	ipa_set_jf_simple_pass_through (jfunc, index);
>         return;
>       }
>   
> --- 897,907 ----
>   	}
>         else if (gimple_assign_single_p (stmt)
>   	       && !detect_type_change_ssa (tc_ssa, call, jfunc))
> ! 	{
> ! 	  bool agg_p = parm_ref_data_pass_through_p (&parms_ainfo[index],
> ! 						     call, tc_ssa);
> ! 	  ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
> ! 	}
>         return;
>       }
>   
> *************** compute_complex_assign_jump_func (struct
> *** 757,763 ****
>     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);
>   }
>   
>   /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
> --- 927,935 ----
>     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,
> ! 			 parm_ref_data_pass_through_p (&parms_ainfo[index],
> ! 						       call, ssa));
>   }
>   
>   /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
> *************** get_ancestor_addr_info (gimple assign, t
> *** 828,833 ****
> --- 1000,1006 ----
>   
>   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)
>   {
> *************** compute_complex_ancestor_jump_func (stru
> *** 881,887 ****
>       }
>   
>     if (!detect_type_change (obj, expr, call, jfunc, offset))
> !     ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index);
>   }
>   
>   /* Given OP which is passed as an actual argument to a called function,
> --- 1054,1062 ----
>       }
>   
>     if (!detect_type_change (obj, expr, call, jfunc, offset))
> !     ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index,
> ! 			 parm_ref_data_pass_through_p (&parms_ainfo[index],
> ! 						       call, parm));
>   }
>   
>   /* Given OP which is passed as an actual argument to a called function,
> *************** compute_known_type_jump_func (tree op, s
> *** 916,970 ****
>     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
> --- 1091,1096 ----
> *************** type_like_member_ptr_p (tree type, tree
> *** 980,993 ****
>   
>     fld = TYPE_FIELDS (type);
>     if (!fld || !POINTER_TYPE_P (TREE_TYPE (fld))
> !       || TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE)
>       return false;
>   
>     if (method_ptr)
>       *method_ptr = fld;
>   
>     fld = DECL_CHAIN (fld);
> !   if (!fld || INTEGRAL_TYPE_P (fld))
>       return false;
>     if (delta)
>       *delta = fld;
> --- 1106,1121 ----
>   
>     fld = TYPE_FIELDS (type);
>     if (!fld || !POINTER_TYPE_P (TREE_TYPE (fld))
> !       || 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)
> !       || !host_integerp (DECL_FIELD_OFFSET (fld), 1))
>       return false;
>     if (delta)
>       *delta = fld;
> *************** type_like_member_ptr_p (tree type, tree
> *** 998,1051 ****
>     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.  */
>   
>   static inline tree
>   get_ssa_def_if_simple_copy (tree rhs)
> --- 1126,1134 ----
>     return true;
>   }
>   
>   /* If RHS is an SSA_NAME and it is defined by a simple copy assign statement,
> !    return the rhs of its defining statement.  Otherwise return RHS as it
> !    is.  */
>   
>   static inline tree
>   get_ssa_def_if_simple_copy (tree rhs)
> *************** get_ssa_def_if_simple_copy (tree rhs)
> *** 1062,1165 ****
>     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;
> - }
>   
> ! /* 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.  */
>   
> ! static void
> ! compute_cst_member_ptr_arguments (struct ipa_edge_args *args,
> ! 				  gimple call)
> ! {
> !   unsigned num;
> !   tree arg, method_field, delta_field;
>   
> !   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 (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);
>       }
>   }
>   
> --- 1145,1357 ----
>     return rhs;
>   }
>   
> ! /* 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
>   {
> +   /* 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 check_ref, by_ref;
> !   ao_ref r;
> ! 
> !   /* The function operates in three stages.  First, we prepare check_ref, r,
> !      arg_base and arg_offset based on what is actually passed as an actual
> !      argument.  */
> ! 
> !   if (POINTER_TYPE_P (TREE_TYPE (arg)))
> !     {
> !       by_ref = true;
> !       if (TREE_CODE (arg) == SSA_NAME)
> ! 	{
> ! 	  tree type_size;
> !           if (!host_integerp (TYPE_SIZE (TREE_TYPE (TREE_TYPE (arg))), 1))
> !             return;
> ! 	  check_ref = true;
> ! 	  arg_base = arg;
> ! 	  arg_offset = 0;
> ! 	  type_size = TYPE_SIZE (TREE_TYPE (TREE_TYPE (arg)));
> ! 	  arg_size = tree_low_cst (type_size, 1);
> ! 	  ao_ref_init_from_ptr_and_size (&r, arg_base, NULL_TREE);
> ! 	}
> !       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))
> ! 	    {
> ! 	      tree size;
> ! 	      check_ref = false;
> ! 	      size = build_int_cst (integer_type_node, arg_size);
> ! 	      ao_ref_init_from_ptr_and_size (&r, arg_base, size);
> ! 	    }
> ! 	  else
> ! 	    return;
> ! 	}
> !       else
> ! 	return;
> !     }
> !   else
> !     {
> !       HOST_WIDE_INT arg_max_size;
>   
> !       gcc_checking_assert (AGGREGATE_TYPE_P (TREE_TYPE (arg)));
>   
> +       by_ref = false;
> +       check_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 (check_ref)
>   	{
> ! 	  if (TREE_CODE (lhs_base) != MEM_REF
> ! 	      || TREE_OPERAND (lhs_base, 0) != arg_base
> ! 	      || !integer_zerop (TREE_OPERAND (lhs_base, 1)))
> ! 	    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)->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.by_ref = by_ref;
> !       jfunc->agg.items = VEC_alloc (ipa_agg_jf_item_t, gc, const_count);
> !       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;
> ! 	}
>       }
>   }
>   
> *************** ipa_compute_jump_functions_for_edge (str
> *** 1174,1196 ****
>     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);
>   
>     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);
>   
> !   /* 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;
>   
> !   /* Finally, let's check whether we actually pass a new constant member
> !      pointer here...  */
> !   compute_cst_member_ptr_arguments (args, call);
>   }
>   
>   /* Compute jump functions for all edges - both direct and indirect - outgoing
> --- 1366,1435 ----
>     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 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);
>   
> !   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);
>   
> !       if (is_gimple_ip_invariant (arg))
> ! 	ipa_set_jf_constant (jfunc, arg);
> !       else if (!is_gimple_reg_type (TREE_TYPE (arg))
> ! 	       && TREE_CODE (arg) == PARM_DECL)
> ! 	{
> ! 	  int index = ipa_get_param_decl_index (info, arg);
> ! 
> ! 	  gcc_assert (index >=0);
> ! 	  /* Aggregate passed by value, check for pass-through, otherwise we
> ! 	     will attempt to fill in aggregate contents later in this
> ! 	     for cycle.  */
> ! 	  if (parm_preserved_before_stmt_p (&parms_ainfo[index], call, arg))
> ! 	    {
> ! 	      ipa_set_jf_simple_pass_through (jfunc, index, false);
> ! 	      continue;
> ! 	    }
> ! 	}
> !       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 = parm_ref_data_pass_through_p (&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))
> ! 	  && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
> ! 	      || (POINTER_TYPE_P (TREE_TYPE (arg)))))
> ! 	determine_known_aggregate_parts (call, arg, jfunc);
> !     }
>   }
>   
>   /* Compute jump functions for all edges - both direct and indirect - outgoing
> *************** ipa_compute_jump_functions (struct cgrap
> *** 1217,1232 ****
>       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.  */
>   
>   static tree
> ! ipa_get_member_ptr_load_param (tree rhs, bool use_delta)
>   {
> !   tree rec, ref_field, ref_offset, fld, fld_offset, ptr_field, delta_field;
>   
>     if (TREE_CODE (rhs) == COMPONENT_REF)
>       {
>         ref_field = TREE_OPERAND (rhs, 1);
> --- 1456,1477 ----
>       ipa_compute_jump_functions_for_edge (parms_ainfo, cs);
>   }
>   
> ! /* 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_stmt_member_ptr_load_param (gimple stmt, bool use_delta,
> ! 				    HOST_WIDE_INT *offset_p)
>   {
> !   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);
> *************** ipa_get_member_ptr_load_param (tree rhs,
> *** 1243,1285 ****
>     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 (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);
>   }
>   
>   /* Returns true iff T is an SSA_NAME defined by a statement.  */
> --- 1488,1511 ----
>     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;
>         return ref_field == fld ? rec : NULL_TREE;
>       }
>     else
> !     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.  */
> *************** ipa_note_param_call (struct cgraph_node
> *** 1305,1312 ****
>   
>     cs = cgraph_edge (node, stmt);
>     cs->indirect_info->param_index = param_index;
> !   cs->indirect_info->anc_offset = 0;
>     cs->indirect_info->polymorphic = 0;
>     return cs;
>   }
>   
> --- 1531,1539 ----
>   
>     cs = cgraph_edge (node, stmt);
>     cs->indirect_info->param_index = param_index;
> !   cs->indirect_info->offset = 0;
>     cs->indirect_info->polymorphic = 0;
> +   cs->indirect_info->agg_contents = 0;
>     return cs;
>   }
>   
> *************** ipa_note_param_call (struct cgraph_node
> *** 1365,1371 ****
>   
>          return (S.*f)(4);
>        }
> ! */
>   
>   static void
>   ipa_analyze_indirect_call_uses (struct cgraph_node *node,
> --- 1592,1600 ----
>   
>          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,
> *************** ipa_analyze_indirect_call_uses (struct c
> *** 1380,1385 ****
> --- 1609,1616 ----
>     gimple branch;
>     int index;
>     basic_block bb, virt_bb, join;
> +   HOST_WIDE_INT offset;
> +   bool by_ref;
>   
>     if (SSA_NAME_IS_DEFAULT_DEF (target))
>       {
> *************** ipa_analyze_indirect_call_uses (struct c
> *** 1390,1409 ****
>         return;
>       }
>   
>     /* Now we need to try to match the complex pattern of calling a member
>        pointer. */
> ! 
> !   if (!POINTER_TYPE_P (TREE_TYPE (target))
>         || TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE)
>       return;
>   
> -   def = SSA_NAME_DEF_STMT (target);
> -   if (gimple_code (def) != GIMPLE_PHI)
> -     return;
> - 
> -   if (gimple_phi_num_args (def) != 2)
> -     return;
> - 
>     /* First, we need to check whether one of these is a load from a member
>        pointer that is a parameter to this function. */
>     n1 = PHI_ARG_DEF (def, 0);
> --- 1621,1647 ----
>         return;
>       }
>   
> +   def = SSA_NAME_DEF_STMT (target);
> +   if (gimple_assign_single_p (def)
> +       && ipa_load_from_parm_agg_1 (info, parms_ainfo, def,
> + 				   gimple_assign_rhs1 (def), &index, &offset,
> + 				   &by_ref))
> +     {
> +       struct cgraph_edge *cs = ipa_note_param_call (node, index, call);
> +       cs->indirect_info->offset = offset;
> +       cs->indirect_info->agg_contents = 1;
> +       cs->indirect_info->by_ref = by_ref;
> +       return;
> +     }
> + 
>     /* 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
>        pointer that is a parameter to this function. */
>     n1 = PHI_ARG_DEF (def, 0);
> *************** ipa_analyze_indirect_call_uses (struct c
> *** 1414,1428 ****
>     d2 = SSA_NAME_DEF_STMT (n2);
>   
>     join = gimple_bb (def);
> !   if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false)))
>       {
> !       if (ipa_get_stmt_member_ptr_load_param (d2, false))
>   	return;
>   
>         bb = EDGE_PRED (join, 0)->src;
>         virt_bb = gimple_bb (d2);
>       }
> !   else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false)))
>       {
>         bb = EDGE_PRED (join, 1)->src;
>         virt_bb = gimple_bb (d1);
> --- 1652,1666 ----
>     d2 = SSA_NAME_DEF_STMT (n2);
>   
>     join = gimple_bb (def);
> !   if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false, &offset)))
>       {
> !       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, &offset)))
>       {
>         bb = EDGE_PRED (join, 1)->src;
>         virt_bb = gimple_bb (d1);
> *************** ipa_analyze_indirect_call_uses (struct c
> *** 1477,1491 ****
>   
>     rec2 = ipa_get_stmt_member_ptr_load_param (def,
>   					     (TARGET_PTRMEMFUNC_VBIT_LOCATION
> ! 					      == ptrmemfunc_vbit_in_delta));
> ! 
>     if (rec != rec2)
>       return;
>   
>     index = ipa_get_param_decl_index (info, rec);
> !   if (index >= 0 && !is_parm_modified_before_stmt (&parms_ainfo[index],
> ! 						   call, rec))
> !     ipa_note_param_call (node, index, call);
>   
>     return;
>   }
> --- 1715,1733 ----
>   
>     rec2 = ipa_get_stmt_member_ptr_load_param (def,
>   					     (TARGET_PTRMEMFUNC_VBIT_LOCATION
> ! 					      == ptrmemfunc_vbit_in_delta),
> ! 					     NULL);
>     if (rec != rec2)
>       return;
>   
>     index = ipa_get_param_decl_index (info, rec);
> !   if (index >= 0
> !       && parm_preserved_before_stmt_p (&parms_ainfo[index], call, rec))
> !     {
> !       struct cgraph_edge *cs = ipa_note_param_call (node, index, call);
> !       cs->indirect_info->offset = offset;
> !       cs->indirect_info->agg_contents = 1;
> !     }
>   
>     return;
>   }
> *************** ipa_analyze_virtual_call_uses (struct cg
> *** 1540,1546 ****
>   
>     cs = ipa_note_param_call (node, index, call);
>     ii = cs->indirect_info;
> !   ii->anc_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;
> --- 1782,1788 ----
>   
>     cs = ipa_note_param_call (node, index, call);
>     ii = cs->indirect_info;
> !   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;
> *************** ipa_analyze_node (struct cgraph_node *no
> *** 1682,1689 ****
>     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);
>   
>     current_function_decl = NULL;
>     pop_cfun ();
> --- 1924,1935 ----
>     ipa_compute_jump_functions (node, parms_ainfo);
>   
>     for (i = 0; i < param_count; i++)
> !     {
> !       if (parms_ainfo[i].parm_visited_statements)
> ! 	BITMAP_FREE (parms_ainfo[i].parm_visited_statements);
> !       if (parms_ainfo[i].pt_visited_statements)
> ! 	BITMAP_FREE (parms_ainfo[i].pt_visited_statements);
> !     }
>   
>     current_function_decl = NULL;
>     pop_cfun ();
> *************** update_jump_functions_after_inlining (st
> *** 1730,1755 ****
>         if (dst->type == IPA_JF_ANCESTOR)
>   	{
>   	  struct ipa_jump_func *src;
>   
>   	  /* 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))
>   	    {
>   	      dst->type = IPA_JF_UNKNOWN;
>   	      continue;
>   	    }
>   
> ! 	  src = ipa_get_ith_jump_func (top, dst->value.ancestor.formal_id);
>   	  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;
>   	  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;
>   	    }
>   	  else
>   	    dst->type = IPA_JF_UNKNOWN;
> --- 1976,2025 ----
>         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_fid >= ipa_get_cs_argument_count (top))
>   	    {
>   	      dst->type = IPA_JF_UNKNOWN;
>   	      continue;
>   	    }
>   
> ! 	  src = ipa_get_ith_jump_func (top, dst_fid);
> ! 
> ! 	  if (src->agg.items
> ! 	      && (dst->value.ancestor.agg_preserved || !src->agg.by_ref))
> ! 	    {
> ! 	      struct ipa_agg_jf_item *item;
> ! 	      int j;
> ! 
> ! 	      /* Currently we do not produce clobber aggregate jump functions,
> ! 		 replace with merging when we do.  */
> ! 	      gcc_assert (!dst->agg.items);
> ! 
> ! 	      dst->agg.items = VEC_copy (ipa_agg_jf_item_t, gc, src->agg.items);
> ! 	      dst->agg.by_ref = src->agg.by_ref;
> ! 	      FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, dst->agg.items, j, item)
> ! 		item->offset -= dst->value.ancestor.offset;
> ! 	    }
> ! 
>   	  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.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;
> *************** update_jump_functions_after_inlining (st
> *** 1763,1771 ****
>   	      && (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;
>   	    }
>   	  else
>   	    dst->type = IPA_JF_UNKNOWN;
> --- 2033,2065 ----
>   	      && (dst->value.pass_through.formal_id
>   		  < ipa_get_cs_argument_count (top)))
>   	    {
> ! 	      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;
> ! 
> ! 	      dst->type = src->type;
> ! 	      dst->value = src->value;
> ! 
> ! 	      if (src->agg.items
> ! 		  && (agg_p || !src->agg.by_ref))
> ! 		{
> ! 		  /* Currently we do not produce clobber aggregate jump
> ! 		     functions, replace with merging when we do.  */
> ! 		  gcc_assert (!dst->agg.items);
> ! 
> ! 		  dst->agg.by_ref = src->agg.by_ref;
> ! 		  dst->agg.items = VEC_copy (ipa_agg_jf_item_t, gc,
> ! 					     src->agg.items);
> ! 		}
> ! 
> ! 	      if (!agg_p)
> ! 		{
> ! 		  if (dst->type == IPA_JF_PASS_THROUGH)
> ! 		    dst->value.pass_through.agg_preserved = false;
> ! 		  else if (dst->type == IPA_JF_ANCESTOR)
> ! 		    dst->value.ancestor.agg_preserved = false;
> ! 		}
>   	    }
>   	  else
>   	    dst->type = IPA_JF_UNKNOWN;
> *************** ipa_make_edge_direct_to_target (struct c
> *** 1812,1817 ****
> --- 2106,2140 ----
>     return ie;
>   }
>   
> + /* Retrieve value from aggregate jump function AGG for the given OFFSET or
> +    return NULL if there is not any.  BY_REF specifies whether the value has to
> +    be passed by reference or by value.  */
> + 
> + tree
> + ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
> + 			    HOST_WIDE_INT offset, bool by_ref)
> + {
> +   struct ipa_agg_jf_item *item;
> +   int i;
> + 
> +   if (by_ref != agg->by_ref)
> +     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 for 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
> *************** try_make_edge_direct_simple_call (struct
> *** 1823,1835 ****
>   {
>     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);
>     else
> !     return NULL;
> ! 
>     return ipa_make_edge_direct_to_target (ie, target);
>   }
>   
> --- 2146,2165 ----
>   {
>     tree target;
>   
> !   if (ie->indirect_info->agg_contents)
> !     {
> !       target = ipa_find_agg_cst_for_param (&jfunc->agg,
> ! 					   ie->indirect_info->offset,
> ! 					   ie->indirect_info->by_ref);
> !       if (!target)
> ! 	return NULL;
> !     }
>     else
> !     {
> !       if (jfunc->type != IPA_JF_CONST)
> ! 	return NULL;
> !       target = ipa_get_jf_constant (jfunc);
> !     }
>     return ipa_make_edge_direct_to_target (ie, target);
>   }
>   
> *************** try_make_edge_direct_virtual_call (struc
> *** 1850,1856 ****
>     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->otr_type);
>     if (binfo)
>       target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token,
> --- 2180,2186 ----
>     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->offset,
>   			       ie->indirect_info->otr_type);
>     if (binfo)
>       target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token,
> *************** update_indirect_edges_after_inlining (st
> *** 1886,1891 ****
> --- 2216,2222 ----
>       {
>         struct cgraph_indirect_call_info *ici = ie->indirect_info;
>         struct ipa_jump_func *jfunc;
> +       int param_index;
>   
>         next_ie = ie->next_callee;
>   
> *************** update_indirect_edges_after_inlining (st
> *** 1899,1912 ****
>   	  continue;
>   	}
>   
> !       jfunc = ipa_get_ith_jump_func (top, ici->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);
>   	}
>         else
>   	/* Either we can find a destination for this edge now or never. */
> --- 2230,2256 ----
>   	  continue;
>   	}
>   
> !       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)
> ! 	{
> ! 	  if (ici->agg_contents
> ! 	      && !ipa_get_jf_pass_through_agg_preserved (jfunc))
> ! 	    ici->param_index = -1;
> ! 	  else
> ! 	    ici->param_index = ipa_get_jf_pass_through_formal_id (jfunc);
> ! 	}
>         else if (jfunc->type == IPA_JF_ANCESTOR)
>   	{
> ! 	  if (ici->agg_contents
> ! 	      && !ipa_get_jf_ancestor_agg_preserved (jfunc))
> ! 	    ici->param_index = -1;
> ! 	  else
> ! 	    {
> ! 	      ici->param_index = ipa_get_jf_ancestor_formal_id (jfunc);
> ! 	      ici->offset += ipa_get_jf_ancestor_offset (jfunc);
> ! 	    }
>   	}
>         else
>   	/* Either we can find a destination for this edge now or never. */
> *************** ipa_node_removal_hook (struct cgraph_nod
> *** 2074,2086 ****
>     ipa_free_node_params_substructures (IPA_NODE_REF (node));
>   }
>   
> ! /* Hook that is called by cgraph.c when a node 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;
>   
>     ipa_check_create_edge_args ();
>   
> --- 2418,2431 ----
>     ipa_free_node_params_substructures (IPA_NODE_REF (node));
>   }
>   
> ! /* 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 ();
>   
> *************** ipa_edge_duplication_hook (struct cgraph
> *** 2089,2094 ****
> --- 2434,2445 ----
>   
>     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.  */
> *************** static void
> *** 2787,2794 ****
>   ipa_write_jump_function (struct output_block *ob,
>   			 struct ipa_jump_func *jump_func)
>   {
> !   streamer_write_uhwi (ob, jump_func->type);
>   
>     switch (jump_func->type)
>       {
>       case IPA_JF_UNKNOWN:
> --- 3138,3148 ----
>   ipa_write_jump_function (struct output_block *ob,
>   			 struct ipa_jump_func *jump_func)
>   {
> !   struct ipa_agg_jf_item *item;
> !   struct bitpack_d bp;
> !   int i, count;
>   
> +   streamer_write_uhwi (ob, jump_func->type);
>     switch (jump_func->type)
>       {
>       case IPA_JF_UNKNOWN:
> *************** ipa_write_jump_function (struct output_b
> *** 2805,2820 ****
>         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);
>         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);
>         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;
>       }
>   }
>   
> --- 3159,3191 ----
>         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;
> !     }
> ! 
> !   count = VEC_length (ipa_agg_jf_item_t, jump_func->agg.items);
> !   streamer_write_uhwi (ob, count);
> !   if (count)
> !     {
> !       bp = bitpack_create (ob->main_stream);
> !       bp_pack_value (&bp, jump_func->agg.by_ref, 1);
> !       streamer_write_bitpack (&bp);
> !     }
> ! 
> !   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);
>       }
>   }
>   
> *************** ipa_read_jump_function (struct lto_input
> *** 2825,2832 ****
>   			struct ipa_jump_func *jump_func,
>   			struct data_in *data_in)
>   {
> !   jump_func->type = (enum jump_func_type) streamer_read_uhwi (ib);
>   
>     switch (jump_func->type)
>       {
>       case IPA_JF_UNKNOWN:
> --- 3196,3205 ----
>   			struct ipa_jump_func *jump_func,
>   			struct data_in *data_in)
>   {
> !   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:
> *************** ipa_read_jump_function (struct lto_input
> *** 2845,2860 ****
>         jump_func->value.pass_through.formal_id = streamer_read_uhwi (ib);
>         jump_func->value.pass_through.operation
>   	= (enum tree_code) streamer_read_uhwi (ib);
>         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);
>         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;
>       }
>   }
>   
> --- 3218,3249 ----
>         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;
> !     }
> ! 
> !   count = streamer_read_uhwi (ib);
> !   jump_func->agg.items = VEC_alloc (ipa_agg_jf_item_t, gc, count);
> !   if (count)
> !     {
> !       bp = streamer_read_bitpack (ib);
> !       jump_func->agg.by_ref = bp_unpack_value (&bp, 1);
> !     }
> !   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);
>       }
>   }
>   
> *************** ipa_write_indirect_edge_info (struct out
> *** 2869,2877 ****
>     struct bitpack_d bp;
>   
>     streamer_write_hwi (ob, ii->param_index);
> !   streamer_write_hwi (ob, ii->anc_offset);
>     bp = bitpack_create (ob->main_stream);
>     bp_pack_value (&bp, ii->polymorphic, 1);
>     streamer_write_bitpack (&bp);
>   
>     if (ii->polymorphic)
> --- 3258,3268 ----
>     struct bitpack_d bp;
>   
>     streamer_write_hwi (ob, ii->param_index);
> !   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);
> +   bp_pack_value (&bp, ii->by_ref, 1);
>     streamer_write_bitpack (&bp);
>   
>     if (ii->polymorphic)
> *************** ipa_read_indirect_edge_info (struct lto_
> *** 2893,2901 ****
>     struct bitpack_d bp;
>   
>     ii->param_index = (int) streamer_read_hwi (ib);
> !   ii->anc_offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
>     bp = streamer_read_bitpack (ib);
>     ii->polymorphic = bp_unpack_value (&bp, 1);
>     if (ii->polymorphic)
>       {
>         ii->otr_token = (HOST_WIDE_INT) streamer_read_hwi (ib);
> --- 3284,3294 ----
>     struct bitpack_d bp;
>   
>     ii->param_index = (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);
> +   ii->by_ref = bp_unpack_value (&bp, 1);
>     if (ii->polymorphic)
>       {
>         ii->otr_token = (HOST_WIDE_INT) streamer_read_hwi (ib);
> Index: src/gcc/cgraph.h
> ===================================================================
> *** src.orig/gcc/cgraph.h
> --- src/gcc/cgraph.h
> *************** typedef enum cgraph_inline_failed_enum {
> *** 338,346 ****
>   
>   struct GTY(()) cgraph_indirect_call_info
>   {
> !   /* Offset accumulated from ancestor jump functions of inlined call graph
> !      edges.  */
> !   HOST_WIDE_INT anc_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. */
> --- 338,348 ----
>   
>   struct GTY(()) cgraph_indirect_call_info
>   {
> !   /* 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. */
> *************** struct GTY(()) cgraph_indirect_call_info
> *** 353,358 ****
> --- 355,366 ----
>     /* 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;
> +   /* When the previous bit is set, this one determines whether the destination
> +      is loaded from a parameter passed by reference. */
> +   unsigned by_ref : 1;
>   };
>   
>   struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge {
> Index: src/gcc/ipa-cp.c
> ===================================================================
> *** src.orig/gcc/ipa-cp.c
> --- src/gcc/ipa-cp.c
> *************** ipa_get_indirect_edge_target (struct cgr
> *** 1107,1113 ****
>       }
>   
>     token = ie->indirect_info->otr_token;
> !   anc_offset = ie->indirect_info->anc_offset;
>     otr_type = ie->indirect_info->otr_type;
>   
>     t = VEC_index (tree, known_vals, param_index);
> --- 1107,1113 ----
>       }
>   
>     token = ie->indirect_info->otr_token;
> !   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 ****
> --- 1,221 ----
> + /* Verify that simple indirect calls are inlined even without early
> +    inlining..  */
> + /* { dg-do compile } */
> + /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
> + 
> + struct S
> + {
> +   int i;
> +   void (*f)(struct S *);
> +   unsigned u;
> + };
> + 
> + struct U
> + {
> +   struct U *next;
> +   struct S s;
> +   short a[8];
> + };
> + 
> + extern void non_existent(struct S *p, int);
> + 
> + /* ----- 1 ----- */
> + 
> + 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.u = 1001;
> +   hiphip1 (&s);
> +   return 0;
> + }
> + 
> + /* ----- 2 ----- */
> + 
> + struct S *gp;
> + 
> + 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->u = 1002;
> +   hiphip2 (p);
> +   return 0;
> + }
> + 
> + /* ----- 3 ----- */
> + 
> + static void hooray3 (struct S *p)
> + {
> +   non_existent (p, 2);
> + }
> + 
> + static void hip3 (struct S *p)
> + {
> +   p->f (p);
> + }
> + 
> + static __attribute__ ((flatten)) void hiphip3 (struct S *p)
> + {
> +   hip3 (p);
> + }
> + 
> + int test3 (void)
> + {
> +   struct S *p = gp;
> +   p->i = 2341;
> +   p->f = hooray3;
> +   p->u = 1003;
> +   hiphip3 (p);
> +   return 0;
> + }
> + 
> + /* ----- 4 ----- */
> + 
> + static void hooray4 (struct S *p)
> + {
> +   non_existent (p, 3);
> + }
> + 
> + static void hiphip4 (struct S s)
> + {
> +   s.f (&s);
> + }
> + 
> + int test4(void)
> + {
> +   struct S s;
> +   s.i = 3412;
> +   s.f = hooray4;
> +   s.u = 1004;
> +   hiphip4 (s);
> +   return 0;
> + }
> + 
> + /* ----- 5 ----- */
> + 
> + struct U *gu;
> + 
> + static void hooray5 (struct S *p)
> + {
> +   non_existent (p, 5);
> + }
> + 
> + static void hip5 (struct S *p)
> + {
> +   p->f (p);
> + }
> + 
> + static void hiphip5 (struct U *u)
> + {
> +   hip5 (&u->s);
> + }
> + 
> + int test5 (void)
> + {
> +   struct U *u = gu;
> +   u->next = u;
> +   u->s.i = 9876;
> +   u->s.f = hooray5;
> +   u->s.u = 1005;
> +   hiphip5 (u);
> +   return 0;
> + }
> + 
> + /* ----- 6 ----- */
> + 
> + static void hooray6 (struct S *p)
> + {
> +   non_existent (p, 6);
> + }
> + 
> + static void hip6 (struct S *p)
> + {
> +   p->f (p);
> + }
> + 
> + static __attribute__ ((flatten)) void hiphip6 (struct U *u)
> + {
> +   hip6 (&u->s);
> + }
> + 
> + int test6 (void)
> + {
> +   struct U *u = gu;
> +   u->next = u;
> +   u->s.i = 9876;
> +   u->s.f = hooray6;
> +   u->s.u = 1006;
> +   hiphip6 (u);
> +   return 0;
> + }
> + 
> + /* ----- 7 ----- */
> + 
> + struct S **gdp;
> + 
> + 
> + static void hooray7 (struct S *p)
> + {
> +   non_existent (p, 7);
> + }
> + 
> + static void hip7 (struct S *p)
> + {
> +   p->f (p);
> +   gdp = &p;
> + }
> + 
> + static void hiphip7 (struct S *p)
> + {
> +   hip7 (p);
> +   gdp = &p;
> + }
> + 
> + int test7 (void)
> + {
> +   struct S *p = gp;
> +   p->i = 7341;
> +   p->f = hooray7;
> +   p->u = 1007;
> +   hiphip7 (p);
> +   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 "hooray3\[^\\n\]*inline copy in test3"  "inline"  } } */
> + /* { dg-final { scan-ipa-dump "hooray4\[^\\n\]*inline copy in test4"  "inline"  } } */
> + /* { dg-final { scan-ipa-dump "hooray5\[^\\n\]*inline copy in test5"  "inline"  } } */
> + /* { dg-final { scan-ipa-dump "hooray6\[^\\n\]*inline copy in test6"  "inline"  } } */
> + /* { dg-final { scan-ipa-dump "hooray7\[^\\n\]*inline copy in test7"  "inline"  } } */
> + /* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: src/gcc/testsuite/gcc.dg/ipa/iinline-5.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/ipa/iinline-5.c
> ***************
> *** 0 ****
> --- 1,124 ----
> + /* Verify that simple indirect calls are inlined even without early
> +    inlining..  */
> + /* { dg-do run } */
> + /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
> + 
> + extern void abort (void);
> + 
> + struct S
> + {
> +   int i;
> +   void (*f)(struct S *);
> +   int j,k,l;
> + };
> + 
> + struct Z
> + {
> +   unsigned u;
> +   void (*f)(struct Z *, int);
> +   struct Z *next;
> + };
> + 
> + static struct Z *gz;
> + static struct S *gs;
> + static int gr = 111;
> + char gc[1024];
> + 
> + static __attribute__ ((noinline, noclone)) struct S *
> + get_s (void)
> + {
> +   return (struct S *) &gc;
> + }
> + 
> + 
> + static void wrong_target_1 (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + static void wrong_target_2 (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + static void wrong_target_3 (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + static void good_target (struct Z *z, int i)
> + {
> +   gr = 0;
> + }
> + 
> + static void good_target_3 (struct S *s)
> + {
> +   gr = 0;
> + }
> + 
> + static void g1 (struct S *s)
> + {
> +   struct Z *z = (struct Z*) s;
> +   z->f (z, 8);
> + }
> + 
> + static void f1 (struct S *s)
> + {
> +   gz->f = good_target;
> +   g1 (s);
> + }
> + 
> + static void g2 (struct Z *z)
> + {
> +   z->f (z, 8);
> + }
> + 
> + static void f2 (struct S *s)
> + {
> +   gz->f = good_target;
> +   g2 ((struct Z*) s);
> + }
> + 
> + static void g3 (struct S *s)
> + {
> +   s->f (s);
> + }
> + 
> + static void h3 (struct Z *z)
> + {
> +   gs->f = good_target_3;
> +   g3 ((struct S *) z);
> + }
> + 
> + static void f3 (struct S *s)
> + {
> +   h3 ((struct Z*) s);
> + }
> + 
> + int main (int argc, char **argv)
> + {
> +   struct S *s = get_s();
> +   s->i = 5678;
> +   s->f = wrong_target_1;
> +   s->j = 1234;
> +   gz = (struct Z *) s;
> +   f1 (s);
> + 
> +   s = get_s();
> +   gz = (struct Z *) s;
> +   s->i = 9999;
> +   s->f = wrong_target_1;
> +   f2 (s);
> + 
> +   s = get_s();
> +   gs = s;
> +   s->i = 9999;
> +   s->f = wrong_target_3;
> +   f3 (s);
> + 
> +   return gr;
> + }
> + 
> + 
> + /* { dg-final { scan-ipa-dump-not "wrong_target\[^\\n\]*inline copy in" "inline"  } } */
> + /* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: src/gcc/testsuite/gcc.dg/ipa/iinline-6.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/ipa/iinline-6.c
> ***************
> *** 0 ****
> --- 1,72 ----
> + /* Verify that simple indirect calls are inlined even without early
> +    inlining..  */
> + /* { dg-do run } */
> + /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
> + 
> + extern void abort (void);
> + 
> + struct S
> + {
> +   int i;
> +   void (*f)(struct S *);
> +   int j,k,l;
> + };
> + 
> + struct Z
> + {
> +   unsigned u;
> +   void (*f)(struct Z *, int);
> +   struct Z *next;
> + };
> + 
> + static struct S *gs;
> + static int gr = 111;
> + char gc[1024];
> + 
> + static __attribute__ ((noinline, noclone)) struct S *
> + get_s (void)
> + {
> +   return (struct S *) &gc;
> + }
> + 
> + static void wrong_target (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + static void good_target (struct S *s)
> + {
> +   gr = 0;
> + }
> + 
> + static void g1 (struct S *s)
> + {
> +   s->f (s);
> + }
> + 
> + static void f2 (struct Z *z)
> + {
> +   gs->f = good_target;
> +   g1 ((struct S *) z);
> + }
> + 
> + static inline __attribute__ ((flatten)) void f1 (struct S *s)
> + {
> +   f2 ((struct Z *) s);
> + }
> + 
> + int main (int argc, char **argv)
> + {
> +   struct S *s = get_s();
> +   s->i = 5678;
> +   s->f = wrong_target;
> +   s->j = 1234;
> +   gs = s;
> +   f1 (s);
> + 
> +   return gr;
> + }
> + 
> + 
> + /* { dg-final { scan-ipa-dump-not "wrong_target\[^\\n\]*inline copy in" "inline"  } } */
> + /* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: src/gcc/testsuite/gcc.dg/ipa/iinline-7.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/ipa/iinline-7.c
> ***************
> *** 0 ****
> --- 1,157 ----
> + /* Verify that simple indirect calls are inlined even without early
> +    inlining..  */
> + /* { dg-do run } */
> + /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
> + 
> + extern void abort (void);
> + 
> + struct S
> + {
> +   int i;
> +   void (*f)(struct S *);
> +   int j,k,l;
> + };
> + 
> + struct U
> + {
> +   struct U *next;
> +   struct S s;
> +   short a[8];
> + };
> + 
> + struct Z
> + {
> +   unsigned u;
> +   void (*f)(struct Z *, int);
> +   struct Z *next;
> + };
> + 
> + static struct Z *gz;
> + static struct U *gu;
> + static int gr = 111;
> + char gc[1024];
> + 
> + static __attribute__ ((noinline, noclone)) struct U *
> + get_u (void)
> + {
> +   return (struct U *) &gc;
> + }
> + 
> + static void wrong_target_1 (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + static void wrong_target_2 (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + static void wrong_target_3 (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + static void wrong_target_4 (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + static void good_target (struct Z *z, int i)
> + {
> +   gr = 0;
> + }
> + 
> + static void good_target_4 (struct S *s)
> + {
> +   gr = 0;
> + }
> + 
> + static void g1 (struct S *s)
> + {
> +   struct Z *z = (struct Z*) s;
> +   z->f (z, 8);
> + }
> + 
> + static void f1 (struct U *u)
> + {
> +   gz->f = good_target;
> +   g1 (&u->s);
> + }
> + 
> + static void g2 (struct Z *z)
> + {
> +   z->f (z, 8);
> + }
> + 
> + static void f2 (struct U *u)
> + {
> +   gz->f = good_target;
> +   g2 ((struct Z*) &u->s);
> + }
> + 
> + static void h3 (struct Z *z)
> + {
> +   z->f (z, 8);
> + }
> + 
> + static void g3 (struct S *s)
> + {
> +   h3 ((struct Z*) s);
> + }
> + 
> + static void f3 (struct U *u)
> + {
> +   gz->f = good_target;
> +   g3 (&u->s);
> + }
> + 
> + static void h4 (struct S *s)
> + {
> +   s->f (s);
> + }
> + 
> + static void g4 (struct U *u)
> + {
> +   h4 (&u->s);
> + }
> + 
> + static inline __attribute__ ((flatten)) void f4 (struct Z *z)
> + {
> +   gu->s.f = good_target_4;
> +   g4 ((struct U *) z);
> + }
> + 
> + int main (int argc, char **argv)
> + {
> +   struct U *u = get_u ();
> +   u->next = u;
> +   u->s.i = 5678;
> +   u->s.f = wrong_target_1;
> +   u->s.j = 1234;
> +   gz = (struct Z *) &u->s;
> +   f1 (u);
> + 
> +   u = get_u();
> +   u->s.i = 9999;
> +   u->s.f = wrong_target_2;
> +   gz = (struct Z *) &u->s;
> +   f2 (u);
> + 
> +   u = get_u();
> +   u->s.i = 9998;
> +   u->s.f = wrong_target_3;
> +   gz = (struct Z *) &u->s;
> +   f3 (u);
> + 
> +   u = get_u();
> +   u->s.i = 9998;
> +   u->s.f = wrong_target_4;
> +   gu = u;
> +   f4 ((struct Z *) u);
> +   return gr;
> + }
> + 
> + 
> + /* { dg-final { scan-ipa-dump-not "wrong_target\[^\\n\]*inline copy in" "inline"  } } */
> + /* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: src/gcc/testsuite/gcc.dg/lto/20120723_0.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/lto/20120723_0.c
> ***************
> *** 0 ****
> --- 1,52 ----
> + /* Make sure that by reference and by value aggregate jump functions do not get
> +    mixed up.  */
> + /* { dg-lto-do run } */
> + /* { dg-lto-options {{-O3 -fno-early-inlining -flto}} } */
> + 
> + extern void abort (void);
> + 
> + struct S
> + {
> +   int i;
> +   void (*f)(struct S *);
> +   int j;
> + };
> + 
> + struct E
> + {
> +   struct S *p;
> + };
> + 
> + struct S *gs;
> + int gr = 111;
> + char gc[1024];
> + 
> + static __attribute__ ((noinline, noclone)) struct S *
> + get_s (void)
> + {
> +   return (struct S *) &gc;
> + }
> + 
> + static void wrong_target (struct S *s)
> + {
> +   abort ();
> + }
> + 
> + void bar (struct S *s)
> + {
> +   s->f (s);
> + }
> + 
> + extern void foo (struct S *s);
> + 
> + int main (int argc, char **argv)
> + {
> +   struct S *s = get_s();
> +   gs = s;
> +   s->i = 5678;
> +   s->f = wrong_target;
> +   s->j = 1234;
> +   foo (s);
> + 
> +   return gr;
> + }
> Index: src/gcc/testsuite/gcc.dg/lto/20120723_1.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/lto/20120723_1.c
> ***************
> *** 0 ****
> --- 1,39 ----
> + /* Make sure that by reference and by value aggregate jump functions do not get
> +    mixed up.  */
> + 
> + extern void abort (void);
> + 
> + struct S
> + {
> +   int i;
> +   void (*f)(struct S *);
> +   int j;
> + };
> + 
> + struct E
> + {
> +   struct S *p;
> + };
> + 
> + extern struct S *gs;
> + extern int gr;
> + extern char gc[1024];
> + 
> + static __attribute__ ((noinline, noclone)) struct S *
> + get_s (void)
> + {
> +   return (struct S *) &gc;
> + }
> + 
> + static void good_target (struct S *s)
> + {
> +   gr = 0;
> + }
> + 
> + extern void bar (struct E e);
> + 
> + void foo (struct E e)
> + {
> +   gs->f = good_target;
> +   bar (e);
> + }
> 
> 

-- 
Richard Guenther <rguenther@suse.de>
SUSE / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746
GF: Jeff Hawn, Jennifer Guild, Felix Imend


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]