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]

[1/3] Build aggregate jump functions


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?

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,

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);
+ }


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