public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH][RFC] Implement IPA points-to and call mod/ref analysis
@ 2009-11-26 16:42 Richard Guenther
  0 siblings, 0 replies; only message in thread
From: Richard Guenther @ 2009-11-26 16:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: Daniel Berlin


On top of the function annotation patches
(http://gcc.gnu.org/ml/gcc-patches/2009-11/msg01257.html and references
therein) this patch implements IPA PTA and as fallout IPA call mod/ref
analysis.

The patch is generally final apart from bugs and a glitch that
currently requires pt_solution_contains_global to be disabled.

Alongside IPA PTA this adds

 - a new dump modifier, -alias, that prints call mod/ref information
   (also useful for debugging the function annotation patches)

 - a second UID for decls (ugh) - I see no easier way to get around
   inlining vs. points-to analysis interactions

Otherwise there are already loads of testcases and most of the
testsuite passes with -fipa-pta (so does SPEC2006 CPU INT where
it doesn't blow away my machine which it does for 403.gcc
and 483.xalancbmk).  I still see some libgomp C++ fails (I guess
I need to fixup alias info explicitly there, the local re-computation
is disabled by IPA-PTA) and there are some libstdc++ fails
which I didn't yet look at (possibly its NRV which I just noticed
is still missing proper IPA constraints).

Otherwise bootstrapped (not with -fipa-pta) and regtested (with
-fipa-pta) on x86_64-unknown-linux-gnu.

I'd like to get both the function annotation patches and the
IPA-PTA patches committed at the beginning of the next stage1,
so if you have any comments or suggestions you have about 2 month
to raise them ;)

Thanks,
Richard.


2009-11-25  Richard Guenther  <rguenther@suse.de>

	* tree-ssa-structalias.c (struct variable_info): Add
	is_fn_info flag.
	(new_var_info): Initialize it.
	(dump_constraints): Support printing last added constraints.
	(debug_constraints): Adjust.
	(dump_constraint_graph): Likewise.
	(make_heapvar_for): Check for NULL cfun.
	(get_function_part_constraint): New function.
	(get_fi_for_callee): Likewise.
	(find_func_aliases): Properly implement IPA PTA constraints.
	(process_ipa_clobber): New function.
	(find_func_clobbers): Likewise.
	(create_function_info_for): Properly allocate vars for IPA mode.
	(create_variable_info_for): Properly generate constraints for
	global vars in IPA mode.
	(dump_solution_for_var): Always dump the solution.
	(set_uids_in_ptset): Initialize DECL_PT_UID if in ipa-mode.
	(find_what_var_points_to): Adjust.
	(pt_solution_set): Change.
	(pt_solution_ior_into): New function.
	(pt_solution_empty_p): Export.
	(pt_solution_includes_global): Adjust.
	(pt_solution_includes_1): Likewise.
	(pt_solutions_intersect_1): Likewise.
	(dump_sa_points_to_info): Check some invariants.
	(solve_constraints): Move constraint dumping ...
	(compute_points_to_sets): ... here.
	(ipa_pta_execute): ... and here.
	(compute_may_aliases): Do not re-compute points-to info
	locally if IPA info is available.
	(ipa_escaped_pt): New global var.
	(ipa_pta_execute): Properly implement IPA PTA.
	* tree-into-ssa.c (dump_decl_set): Support dumping
	decls not in referenced-vars.
	* tree-flow.h (struct gimple_df): Add ipa_pta flag.
	* tree-ssa-alias.c (ptr_deref_may_alias_decl_p): Adjust.
	(dump_points_to_solution): Likewise.
	* tree-dfa.c (dump_variable): Also dump DECL_PT_UID.
	* tree-inline.c (remap_ssa_name): Copy IPA points-to solution.
	(remap_gimple_stmt): Reset call clobber/use information if
	necessary.
	(copy_decl_to_var): Copy DECL_PT_UID.
	(copy_result_decl_to_var): Likewise.
	* tree.c (make_node_stat): Initialize DECL_PT_UID.
	(copy_node_stat): Copy it.
	* tree.h (DECL_PT_UID): New macro.
	(SET_DECL_PT_UID): Likewise.
	(struct tree_decl_minimal): Add pt_uid member.
	* tree-ssa-alias.h (struct pt_solution): Add ipa_escaped flag.
	(pt_solution_empty_p): Declare.
	(pt_solution_set): Adjust.
	(ipa_escaped_pt): Declare.
	* cfgexpand.c (update_alias_info_with_stack_vars): Adjust.
	* gimple-pretty-print.c (pp_points_to_solution): New function.
	(dump_gimple_call): Dump call clobber/use information.
	* tree-dump.c (dump_option_value_in): Add TDF_ALIAS entry.
	* tree-pass.h (TDF_ALIAS): New dump option.

	* gcc.dg/ipa/ipa-pta-1.c: New testcase.
	* gcc.dg/ipa/ipa-pta-2.c: Likewise.
	* gcc.dg/ipa/ipa-pta-3.c: Likewise.
	* gcc.dg/ipa/ipa-pta-4.c: Likewise.
	* gcc.dg/ipa/ipa-pta-5.c: Likewise.
	* gcc.dg/ipa/ipa-pta-6.c: Likewise.
	* gcc.dg/ipa/ipa-pta-7.c: Likewise.
	* gcc.dg/ipa/ipa-pta-8.c: Likewise.
	* gcc.dg/ipa/ipa-pta-9.c: Likewise.
	* gcc.dg/ipa/ipa-pta-10.c: Likewise.
	* gcc.dg/ipa/ipa-pta-11.c: Likewise.
	* gcc.dg/ipa/ipa-pta-12.c: Likewise.
	* gcc.dg/ipa/ipa-pta-13.c: Likewise.
	* gcc.dg/torture/ipa-pta-2.c: Likewise.
	* gcc.dg/torture/ipa-pta-1.c: Adjust.

Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,50 ----
+ /* { dg-do run } */
+ /* { dg-options "-O -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ static int __attribute__((noinline))
+ foo (int *p, int *q)
+ {
+   *p = 2;
+   *q = 1;
+   return *p;
+ }
+ 
+ static int __attribute__((noinline))
+ bar (int *p, int *q)
+ {
+   *p = -2;
+   *q = -1;
+   return *p;
+ }
+ 
+ static int __attribute__((noinline,noclone))
+ foobar (int foo_p)
+ {
+   int a;
+   int (*fn)(int *, int *);
+   if (foo_p)
+     fn = foo;
+   else
+     fn = bar;
+   return (*fn)(&a, &a);
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   if (foobar (1) != 1)
+     abort ();
+ 
+   return 0;
+ }
+ 
+ /* IPA PTA needs to handle indirect calls properly.  Verify that
+    both bar and foo get a (and only a) in their arguments points-to sets.  */
+ 
+ /* { dg-final { scan-ipa-dump "fn_1 = { bar foo }" "pta" } } */
+ /* { dg-final { scan-ipa-dump "bar.arg0 = { a }" "pta" } } */
+ /* { dg-final { scan-ipa-dump "bar.arg1 = { a }" "pta" } } */
+ /* { dg-final { scan-ipa-dump "foo.arg0 = { a }" "pta" } } */
+ /* { dg-final { scan-ipa-dump "foo.arg1 = { a }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,25 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ int (*fn)(int *);
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int *p)
+ {
+   return *p;
+ }
+ 
+ extern void bar (void);
+ 
+ int main()
+ {
+   fn = foo;
+   bar ();
+   return 0;
+ }
+ 
+ /* Make sure that when a local function escapes its argument points-to sets
+    are properly adjusted.  */
+ 
+ /* { dg-final { scan-ipa-dump "foo.arg0 = { ESCAPED NONLOCAL }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,28 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre-details" } */
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int *p, int *q)
+ {
+   *p = 1;
+   *q = 0;
+   return *p;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   int a, b;
+   if (foo (&a, &b) != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* Verify we can disambiguate *p and *q in foo.  */
+ 
+ /* { dg-final { scan-ipa-dump "foo.arg0 = &a" "pta" } } */
+ /* { dg-final { scan-ipa-dump "foo.arg1 = &b" "pta" } } */
+ /* { dg-final { scan-tree-dump "Replaced \\\*p_1\\\(D\\\) with 1" "fre" } } */
+ /* { dg-final { cleanup-tree-dump "fre" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,33 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre-details" } */
+ 
+ int a, b;
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int *p, int *q)
+ {
+   int res;
+   *p = 1;
+   *q = 0;
+   res = *p;
+   a = 1;
+   b = 1;
+   return res;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   if (foo (&a, &b) != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* Verify we can disambiguate *p and *q in foo.  */
+ 
+ /* { dg-final { scan-ipa-dump "foo.arg0 = &a" "pta" } } */
+ /* { dg-final { scan-ipa-dump "foo.arg1 = &b" "pta" } } */
+ /* { dg-final { scan-tree-dump "Replaced \\\*p_1\\\(D\\\) with 1" "fre" } } */
+ /* { dg-final { cleanup-tree-dump "fre" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,26 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ int **x;
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int **q)
+ {
+   int a = 1;
+   **q = 0;
+   *x = &a;
+   return **q;
+ }
+ 
+ extern void abort (void);
+ int main()
+ {
+   int b;
+   int *p = &b;
+   x = &p;
+   if (foo (&p) != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/torture/ipa-pta-2.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/torture/ipa-pta-2.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,24 ----
+ /* { dg-do run } */
+ /* { dg-options "-fipa-pta" } */
+ 
+ int **x;
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int **p)
+ {
+   int a = 1;
+   **p = 0;
+   *x = &a;
+   return **p;
+ }
+ 
+ extern void abort (void);
+ int main()
+ {
+   int b;
+   int *p = &b;
+   x = &p;
+   if (foo (&p) != 1)
+     abort ();
+   return 0;
+ }
Index: trunk/gcc/tree-ssa-structalias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-structalias.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-ssa-structalias.c	2009-11-26 16:59:32.000000000 +0100
***************
*** 160,165 ****
--- 160,207 ----
    TODO: We could handle unions, but to be honest, it's probably not
    worth the pain or slowdown.  */
  
+ /* IPA-PTA optimizations possible.
+ 
+    When the indirect function called is ANYTHING we can add disambiguation
+    based on the function signatures (or simply the parameter count which
+    is the varinfo size).  We also do not need to consider functions that
+    do not have their address taken.
+ 
+    The is_global_var bit which marks escape points is overly conservative
+    in IPA mode.  Split it to is_escape_point and is_global_var - only
+    externally visible globals are escape points in IPA mode.  This is
+    also needed to fix the pt_solution_includes_global predicate
+    (and thus ptr_deref_may_alias_global_p).
+ 
+    The way we introduce DECL_PT_UID to avoid fixing up all points-to
+    sets in the translation unit when we copy a DECL during inlining
+    pessimizes precision.  The advantage is that the DECL_PT_UID keeps
+    compile-time and memory usage overhead low - the points-to sets
+    do not grow or get unshared as they would during a fixup phase.
+    An alternative solution is to delay IPA PTA until after all
+    inlining transformations have been applied.
+ 
+    The way we propagate clobber/use information isn't optimized.
+    It should use a new complex constraint that properly filters
+    out local variables of the callee (though that would make
+    the sets invalid after inlining).  OTOH we might as well
+    admit defeat to WHOPR and simply do all the clobber/use analysis
+    and propagation after PTA finished but before we threw away
+    points-to information for memory variables.  WHOPR and PTA
+    do not play along well anyway - the whole constraint solving
+    would need to be done in WPA phase and it will be very interesting
+    to apply the results to local SSA names during LTRANS phase.
+ 
+    We probably should compute a per-function unit-ESCAPE solution
+    propagating it simply like the clobber / uses solutions.  The
+    solution can go alongside the non-IPA espaced solution and be
+    used to query which vars escape the unit through a function.
+ 
+    We never put function decls in points-to sets so we do not
+    keep the set of called functions for indirect calls.
+ 
+    And probably more.  */
+ 
  static GTY ((if_marked ("tree_map_marked_p"), param_is (struct tree_map)))
  htab_t heapvar_for_stmt;
  
*************** struct variable_info
*** 235,240 ****
--- 277,285 ----
    /* True if this represents a global variable.  */
    unsigned int is_global_var : 1;
  
+   /* True if this represents a IPA function info.  */
+   unsigned int is_fn_info : 1;
+ 
    /* A link to the variable for the next field in this structure.  */
    struct variable_info *next;
  
*************** new_var_info (tree t, const char *name)
*** 367,372 ****
--- 412,418 ----
    ret->is_restrict_var = false;
    ret->may_have_pointers = true;
    ret->is_global_var = (t == NULL_TREE);
+   ret->is_fn_info = false;
    if (t && DECL_P (t))
      ret->is_global_var = is_global_var (t);
    ret->solution = BITMAP_ALLOC (&pta_obstack);
*************** debug_constraint (constraint_t c)
*** 676,686 ****
  /* Print out all constraints to FILE */
  
  static void
! dump_constraints (FILE *file)
  {
    int i;
    constraint_t c;
!   for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
      dump_constraint (file, c);
  }
  
--- 722,732 ----
  /* Print out all constraints to FILE */
  
  static void
! dump_constraints (FILE *file, int from)
  {
    int i;
    constraint_t c;
!   for (i = from; VEC_iterate (constraint_t, constraints, i, c); i++)
      dump_constraint (file, c);
  }
  
*************** dump_constraints (FILE *file)
*** 689,695 ****
  void
  debug_constraints (void)
  {
!   dump_constraints (stderr);
  }
  
  /* Print out to FILE the edge in the constraint graph that is created by
--- 735,741 ----
  void
  debug_constraints (void)
  {
!   dump_constraints (stderr, 0);
  }
  
  /* Print out to FILE the edge in the constraint graph that is created by
*************** dump_constraint_graph (FILE *file)
*** 740,746 ****
    /* Print the constraints used to produce the constraint graph. The
       constraints will be printed as comments in the dot file:  */
    fprintf (file, "\n\n/* Constraints used in the constraint graph:\n");
!   dump_constraints (file);
    fprintf (file, "*/\n");
  
    /* Prints the header of the dot file:  */
--- 786,792 ----
    /* Print the constraints used to produce the constraint graph. The
       constraints will be printed as comments in the dot file:  */
    fprintf (file, "\n\n/* Constraints used in the constraint graph:\n");
!   dump_constraints (file, 0);
    fprintf (file, "*/\n");
  
    /* Prints the header of the dot file:  */
*************** make_heapvar_for (varinfo_t lhs, const c
*** 3510,3516 ****
  
    /* For global vars we need to add a heapvar to the list of referenced
       vars of a different function than it was created for originally.  */
!   if (gimple_referenced_vars (cfun))
      add_referenced_var (heapvar);
  
    vi = new_var_info (heapvar, name);
--- 3556,3562 ----
  
    /* For global vars we need to add a heapvar to the list of referenced
       vars of a different function than it was created for originally.  */
!   if (cfun && gimple_referenced_vars (cfun))
      add_referenced_var (heapvar);
  
    vi = new_var_info (heapvar, name);
*************** make_constraint_from_restrict (varinfo_t
*** 3553,3558 ****
--- 3599,3647 ----
    vi->may_have_pointers = 0;
  }
  
+ /* In IPA mode there are varinfos for different aspects of reach
+    function designator.  One for the points-to set of the return
+    value, one for the variables that are clobbered by the function,
+    one for its uses and one for each parameter (including a single
+    glob for remaining variadic arguments).
+    ???  We miss the static chain and the by-reference result decl here.  */
+ 
+ enum { fi_clobbers = 1, fi_uses = 2,
+        fi_static_chain = 3, fi_result = 4, fi_parm_base = 5 };
+ 
+ /* Get a constraint for the requested part of a function designator FI
+    when operating in IPA mode.  */
+ 
+ static struct constraint_expr
+ get_function_part_constraint (varinfo_t fi, unsigned part)
+ {
+   struct constraint_expr c;
+ 
+   gcc_assert (in_ipa_mode);
+ 
+   if (fi->id == anything_id)
+     {
+       /* ???  We probably should have a ANYFN special variable.  */
+       c.var = anything_id;
+       c.offset = 0;
+       c.type = SCALAR;
+     }
+   else if (TREE_CODE (fi->decl) == FUNCTION_DECL)
+     {
+       c.var = first_vi_for_offset (fi, part)->id;
+       c.offset = 0;
+       c.type = SCALAR;
+     }
+   else
+     {
+       c.var = fi->id;
+       c.offset = part;
+       c.type = DEREF;
+     }
+ 
+   return c;
+ }
+ 
  /* For non-IPA mode, generate constraints necessary for a call on the
     RHS.  */
  
*************** handle_pure_call (gimple stmt, VEC(ce_s,
*** 3798,3803 ****
--- 3887,3926 ----
    VEC_safe_push (ce_s, heap, *results, &rhsc);
  }
  
+ 
+ /* Return the varinfo for the callee of CALL.  */
+ 
+ static varinfo_t
+ get_fi_for_callee (gimple call)
+ {
+   tree decl;
+ 
+   /* If we can directly resolve the function being called, do so.
+      Otherwise, it must be some sort of indirect expression that
+      we should still be able to handle.  */
+   decl = gimple_call_fndecl (call);
+   if (decl)
+     return get_vi_for_tree (decl);
+ 
+   decl = gimple_call_fn (call);
+   /* The function can be either an SSA name pointer or,
+      worse, an OBJ_TYPE_REF.  In this case we have no
+      clue and should be getting ANYFN (well, ANYTHING for now).  */
+   if (TREE_CODE (decl) == SSA_NAME)
+     {
+       if (TREE_CODE (decl) == SSA_NAME
+ 	  && TREE_CODE (SSA_NAME_VAR (decl)) == PARM_DECL
+ 	  && SSA_NAME_IS_DEFAULT_DEF (decl))
+ 	decl = SSA_NAME_VAR (decl);
+       return get_vi_for_tree (decl);
+     }
+   else if (TREE_CODE (decl) == INTEGER_CST
+ 	   || TREE_CODE (decl) == OBJ_TYPE_REF)
+     return get_varinfo (anything_id);
+   else
+     gcc_unreachable ();
+ }
+ 
  /* Walk statement T setting up aliasing constraints according to the
     references found in T.  This function is the main part of the
     constraint builder.  AI points to auxiliary alias information used
*************** find_func_aliases (gimple origt)
*** 3810,3815 ****
--- 3933,3939 ----
    VEC(ce_s, heap) *lhsc = NULL;
    VEC(ce_s, heap) *rhsc = NULL;
    struct constraint_expr *c;
+   varinfo_t fi;
  
    /* Now build constraints expressions.  */
    if (gimple_code (t) == GIMPLE_PHI)
*************** find_func_aliases (gimple origt)
*** 3964,3969 ****
--- 4088,4175 ----
  	  case BUILT_IN_REMQUOL:
  	  case BUILT_IN_FREE:
  	    return;
+ 	  /* Trampolines are special - they set up passing the static
+ 	     frame.  */
+ 	  case BUILT_IN_INIT_TRAMPOLINE:
+ 	    {
+ 	      tree tramp = gimple_call_arg (t, 0);
+ 	      tree nfunc = gimple_call_arg (t, 1);
+ 	      tree frame = gimple_call_arg (t, 2);
+ 	      unsigned i;
+ 	      struct constraint_expr lhs, *rhsp;
+ 	      if (in_ipa_mode)
+ 		{
+ 		  varinfo_t nfi = NULL;
+ 		  gcc_assert (TREE_CODE (nfunc) == ADDR_EXPR);
+ 		  nfi = lookup_vi_for_tree (TREE_OPERAND (nfunc, 0));
+ 		  if (nfi)
+ 		    {
+ 		      lhs = get_function_part_constraint (nfi, fi_static_chain);
+ 		      get_constraint_for (frame, &rhsc);
+ 		      for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); ++i)
+ 			process_constraint (new_constraint (lhs, *rhsp));
+ 		      VEC_free (ce_s, heap, rhsc);
+ 
+ 		      /* Make the frame point to the function for
+ 			 the trampoline adjustment call.  */
+ 		      get_constraint_for (tramp, &lhsc);
+ 		      do_deref (&lhsc);
+ 		      get_constraint_for (nfunc, &rhsc);
+ 		      process_all_all_constraints (lhsc, rhsc);
+ 		      VEC_free (ce_s, heap, rhsc);
+ 		      VEC_free (ce_s, heap, lhsc);
+ 
+ 		      return;
+ 		    }
+ 		}
+ 	      /* Else fallthru to generic handling which will let
+ 	         the frame escape.  */
+ 	      break;
+ 	    }
+ 	  case BUILT_IN_ADJUST_TRAMPOLINE:
+ 	    {
+ 	      tree tramp = gimple_call_arg (t, 0);
+ 	      tree res = gimple_call_lhs (t);
+ 	      if (in_ipa_mode && res)
+ 		{
+ 		  get_constraint_for (res, &lhsc);
+ 		  get_constraint_for (tramp, &rhsc);
+ 		  do_deref (&rhsc);
+ 		  process_all_all_constraints (lhsc, rhsc);
+ 		  VEC_free (ce_s, heap, rhsc);
+ 		  VEC_free (ce_s, heap, lhsc);
+ 		}
+ 	      return;
+ 	    }
+ 	  /* Variadic argument handling needs to be handled in IPA
+ 	     mode as well.  */
+ 	  case BUILT_IN_VA_START:
+ 	    {
+ 	      if (in_ipa_mode)
+ 		{
+ 		  tree valist = gimple_call_arg (t, 0);
+ 		  struct constraint_expr rhs, *lhsp;
+ 		  unsigned i;
+ 		  /* The va_list gets access to pointers in variadic
+ 		     arguments.  */
+ 		  fi = lookup_vi_for_tree (cfun->decl);
+ 		  gcc_assert (fi != NULL);
+ 		  get_constraint_for (valist, &lhsc);
+ 		  do_deref (&lhsc);
+ 		  rhs = get_function_part_constraint (fi, ~0);
+ 		  rhs.type = ADDRESSOF;
+ 		  for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); ++i)
+ 		    process_constraint (new_constraint (*lhsp, rhs));
+ 		  VEC_free (ce_s, heap, lhsc);
+ 		  /* va_list is clobbered.  */
+ 		  make_constraint_to (get_call_clobber_vi (t)->id, valist);
+ 		  return;
+ 		}
+ 	      break;
+ 	    }
+ 	  /* va_end doesn't have any effect that matters.  */
+ 	  case BUILT_IN_VA_END:
+ 	    return;
  	  /* printf-style functions may have hooks to set pointers to
  	     point to somewhere into the generated string.  Leave them
  	     for a later excercise...  */
*************** find_func_aliases (gimple origt)
*** 3972,3978 ****
  	  }
        if (!in_ipa_mode
  	  || (fndecl
! 	      && !lookup_vi_for_tree (fndecl)))
  	{
  	  VEC(ce_s, heap) *rhsc = NULL;
  	  int flags = gimple_call_flags (t);
--- 4178,4185 ----
  	  }
        if (!in_ipa_mode
  	  || (fndecl
! 	      && (!(fi = lookup_vi_for_tree (fndecl))
! 		  || !fi->is_fn_info)))
  	{
  	  VEC(ce_s, heap) *rhsc = NULL;
  	  int flags = gimple_call_flags (t);
*************** find_func_aliases (gimple origt)
*** 4000,4023 ****
        else
  	{
  	  tree lhsop;
! 	  varinfo_t fi;
! 	  int i = 1;
! 	  size_t j;
! 	  tree decl;
! 
! 	  lhsop = gimple_call_lhs (t);
! 	  decl = gimple_call_fndecl (t);
  
! 	  /* If we can directly resolve the function being called, do so.
! 	     Otherwise, it must be some sort of indirect expression that
! 	     we should still be able to handle.  */
! 	  if (decl)
! 	    fi = get_vi_for_tree (decl);
! 	  else
! 	    {
! 	      decl = gimple_call_fn (t);
! 	      fi = get_vi_for_tree (decl);
! 	    }
  
  	  /* Assign all the passed arguments to the appropriate incoming
  	     parameters of the function.  */
--- 4207,4215 ----
        else
  	{
  	  tree lhsop;
! 	  unsigned j;
  
! 	  fi = get_fi_for_callee (t);
  
  	  /* Assign all the passed arguments to the appropriate incoming
  	     parameters of the function.  */
*************** find_func_aliases (gimple origt)
*** 4027,4077 ****
  	      struct constraint_expr *rhsp;
  	      tree arg = gimple_call_arg (t, j);
  
  	      get_constraint_for (arg, &rhsc);
! 	      if (TREE_CODE (decl) != FUNCTION_DECL)
! 		{
! 		  lhs.type = DEREF;
! 		  lhs.var = fi->id;
! 		  lhs.offset = i;
! 		}
! 	      else
! 		{
! 		  lhs.type = SCALAR;
! 		  lhs.var = first_vi_for_offset (fi, i)->id;
! 		  lhs.offset = 0;
! 		}
  	      while (VEC_length (ce_s, rhsc) != 0)
  		{
  		  rhsp = VEC_last (ce_s, rhsc);
  		  process_constraint (new_constraint (lhs, *rhsp));
  		  VEC_pop (ce_s, rhsc);
  		}
- 	      i++;
  	    }
  
  	  /* If we are returning a value, assign it to the result.  */
! 	  if (lhsop)
  	    {
  	      struct constraint_expr rhs;
  	      struct constraint_expr *lhsp;
- 	      unsigned int j = 0;
  
  	      get_constraint_for (lhsop, &lhsc);
! 	      if (TREE_CODE (decl) != FUNCTION_DECL)
! 		{
! 		  rhs.type = DEREF;
! 		  rhs.var = fi->id;
! 		  rhs.offset = i;
! 		}
! 	      else
! 		{
! 		  rhs.type = SCALAR;
! 		  rhs.var = first_vi_for_offset (fi, i)->id;
! 		  rhs.offset = 0;
! 		}
  	      for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
  		process_constraint (new_constraint (*lhsp, rhs));
  	    }
  	}
      }
    /* Otherwise, just a regular assignment statement.  Only care about
--- 4219,4262 ----
  	      struct constraint_expr *rhsp;
  	      tree arg = gimple_call_arg (t, j);
  
+ 	      if (!could_have_pointers (arg))
+ 		continue;
+ 
  	      get_constraint_for (arg, &rhsc);
! 	      lhs = get_function_part_constraint (fi, fi_parm_base + j);
  	      while (VEC_length (ce_s, rhsc) != 0)
  		{
  		  rhsp = VEC_last (ce_s, rhsc);
  		  process_constraint (new_constraint (lhs, *rhsp));
  		  VEC_pop (ce_s, rhsc);
  		}
  	    }
  
  	  /* If we are returning a value, assign it to the result.  */
! 	  lhsop = gimple_call_lhs (t);
! 	  if (lhsop
! 	      && could_have_pointers (lhsop))
  	    {
  	      struct constraint_expr rhs;
  	      struct constraint_expr *lhsp;
  
  	      get_constraint_for (lhsop, &lhsc);
! 	      rhs = get_function_part_constraint (fi, fi_result);
  	      for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
  		process_constraint (new_constraint (*lhsp, rhs));
  	    }
+ 
+ 	  /* If we use a static chain, pass it along.  */
+ 	  if (gimple_call_chain (t))
+ 	    {
+ 	      struct constraint_expr lhs;
+ 	      struct constraint_expr *rhsp;
+ 
+ 	      get_constraint_for (gimple_call_chain (t), &rhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_static_chain);
+ 	      for (j = 0; VEC_iterate (ce_s, rhsc, j, rhsp); j++)
+ 		process_constraint (new_constraint (lhs, *rhsp));
+ 	    }
  	}
      }
    /* Otherwise, just a regular assignment statement.  Only care about
*************** find_func_aliases (gimple origt)
*** 4135,4141 ****
  	   && gimple_return_retval (t) != NULL_TREE
  	   && could_have_pointers (gimple_return_retval (t)))
      {
!       make_escape_constraint (gimple_return_retval (t));
      }
    /* Handle asms conservatively by adding escape constraints to everything.  */
    else if (gimple_code (t) == GIMPLE_ASM)
--- 4320,4341 ----
  	   && gimple_return_retval (t) != NULL_TREE
  	   && could_have_pointers (gimple_return_retval (t)))
      {
!       fi = NULL;
!       if (!in_ipa_mode
! 	  || !(fi = get_vi_for_tree (cfun->decl)))
! 	make_escape_constraint (gimple_return_retval (t));
!       else if (in_ipa_mode
! 	       && fi != NULL)
! 	{
! 	  struct constraint_expr lhs ;
! 	  struct constraint_expr *rhsp;
! 	  unsigned i;
! 
! 	  lhs = get_function_part_constraint (fi, fi_result);
! 	  get_constraint_for (gimple_return_retval (t), &rhsc);
! 	  for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); i++)
! 	    process_constraint (new_constraint (lhs, *rhsp));
! 	}
      }
    /* Handle asms conservatively by adding escape constraints to everything.  */
    else if (gimple_code (t) == GIMPLE_ASM)
*************** find_func_aliases (gimple origt)
*** 4204,4209 ****
--- 4404,4686 ----
  }
  
  
+ /* Create a constraint adding to the clobber set of FI the memory
+    pointed to by PTR.  */
+ 
+ static void
+ process_ipa_clobber (varinfo_t fi, tree ptr)
+ {
+   VEC(ce_s, heap) *ptrc = NULL;
+   struct constraint_expr *c, lhs;
+   unsigned i;
+   get_constraint_for (ptr, &ptrc);
+   lhs = get_function_part_constraint (fi, fi_clobbers);
+   for (i = 0; VEC_iterate (ce_s, ptrc, i, c); i++)
+     process_constraint (new_constraint (lhs, *c));
+   VEC_free (ce_s, heap, ptrc);
+ }
+ 
+ /* Walk statement T setting up clobber and use constraints according to the
+    references found in T.  This function is a main part of the
+    IPA constraint builder.  */
+ 
+ static void
+ find_func_clobbers (gimple origt)
+ {
+   gimple t = origt;
+   VEC(ce_s, heap) *lhsc = NULL;
+   VEC(ce_s, heap) *rhsc = NULL;
+   varinfo_t fi;
+ 
+   /* Add constraints for clobbered/used in IPA mode.
+      We are not interested in what automatic variables are clobbered
+      or used as we only use the information in the caller to which
+      they do not escape.  */
+   gcc_assert (in_ipa_mode);
+ 
+   /* If the stmt refers to memory in any way it better had a VUSE.  */
+   if (gimple_vuse (t) == NULL_TREE)
+     return;
+ 
+   /* We'd better have function information for the current function.  */
+   fi = lookup_vi_for_tree (cfun->decl);
+   gcc_assert (fi != NULL);
+ 
+   /* Account for stores in assignments and calls.  */
+   if (gimple_vdef (t) != NULL_TREE
+       && gimple_has_lhs (t))
+     {
+       tree lhs = gimple_get_lhs (t);
+       tree tem = lhs;
+       while (handled_component_p (tem))
+ 	tem = TREE_OPERAND (tem, 0);
+       if ((DECL_P (tem)
+ 	   && !auto_var_in_fn_p (tem, cfun->decl))
+ 	  || INDIRECT_REF_P (tem))
+ 	{
+ 	  struct constraint_expr lhsc, *rhsp;
+ 	  unsigned i;
+ 	  lhsc = get_function_part_constraint (fi, fi_clobbers);
+ 	  get_constraint_for_address_of (lhs, &rhsc);
+ 	  for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); i++)
+ 	    process_constraint (new_constraint (lhsc, *rhsp));
+ 	  VEC_free (ce_s, heap, rhsc);
+ 	}
+     }
+ 
+   /* Account for uses in assigments and returns.  */
+   if (gimple_assign_single_p (t)
+       || (gimple_code (t) == GIMPLE_RETURN
+ 	  && gimple_return_retval (t) != NULL_TREE))
+     {
+       tree rhs = (gimple_assign_single_p (t)
+ 		  ? gimple_assign_rhs1 (t) : gimple_return_retval (t));
+       tree tem = rhs;
+       while (handled_component_p (tem))
+ 	tem = TREE_OPERAND (tem, 0);
+       if ((DECL_P (tem)
+ 	   && !auto_var_in_fn_p (tem, cfun->decl))
+ 	  || INDIRECT_REF_P (tem))
+ 	{
+ 	  struct constraint_expr lhs, *rhsp;
+ 	  unsigned i;
+ 	  lhs = get_function_part_constraint (fi, fi_uses);
+ 	  get_constraint_for_address_of (rhs, &rhsc);
+ 	  for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); i++)
+ 	    process_constraint (new_constraint (lhs, *rhsp));
+ 	  VEC_free (ce_s, heap, rhsc);
+ 	}
+     }
+ 
+   if (is_gimple_call (t))
+     {
+       varinfo_t cfi = NULL;
+       tree decl = gimple_call_fndecl (t);
+       struct constraint_expr lhs, rhs;
+       unsigned i, j;
+ 
+       /* For builtins we do not have separate function info.  For those
+ 	 we do not generate escapes for we have to generate clobbers/uses.  */
+       if (decl
+ 	  && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
+ 	switch (DECL_FUNCTION_CODE (decl))
+ 	  {
+ 	  /* The following functions use and clobber memory pointed to
+ 	     by their arguments.  */
+ 	  case BUILT_IN_STRCPY:
+ 	  case BUILT_IN_STRNCPY:
+ 	  case BUILT_IN_BCOPY:
+ 	  case BUILT_IN_MEMCPY:
+ 	  case BUILT_IN_MEMMOVE:
+ 	  case BUILT_IN_MEMPCPY:
+ 	  case BUILT_IN_STPCPY:
+ 	  case BUILT_IN_STPNCPY:
+ 	  case BUILT_IN_STRCAT:
+ 	  case BUILT_IN_STRNCAT:
+ 	    {
+ 	      tree dest = gimple_call_arg (t, (DECL_FUNCTION_CODE (decl)
+ 					       == BUILT_IN_BCOPY ? 1 : 0));
+ 	      tree src = gimple_call_arg (t, (DECL_FUNCTION_CODE (decl)
+ 					      == BUILT_IN_BCOPY ? 0 : 1));
+ 	      unsigned i;
+ 	      struct constraint_expr *rhsp, *lhsp;
+ 	      get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_clobbers);
+ 	      for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); i++)
+ 		process_constraint (new_constraint (lhs, *lhsp));
+ 	      VEC_free (ce_s, heap, lhsc);
+ 	      get_constraint_for_ptr_offset (src, NULL_TREE, &rhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_uses);
+ 	      for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); i++)
+ 		process_constraint (new_constraint (lhs, *rhsp));
+ 	      VEC_free (ce_s, heap, rhsc);
+ 	      return;
+ 	    }
+ 	  /* The following function clobbers memory pointed to by
+ 	     its argument.  */
+ 	  case BUILT_IN_MEMSET:
+ 	    {
+ 	      tree dest = gimple_call_arg (t, 0);
+ 	      unsigned i;
+ 	      ce_s *lhsp;
+ 	      get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_clobbers);
+ 	      for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); i++)
+ 		process_constraint (new_constraint (lhs, *lhsp));
+ 	      VEC_free (ce_s, heap, lhsc);
+ 	      return;
+ 	    }
+ 	  /* The following functions clobber their second and third
+ 	     arguments.  */
+ 	  case BUILT_IN_SINCOS:
+ 	  case BUILT_IN_SINCOSF:
+ 	  case BUILT_IN_SINCOSL:
+ 	    {
+ 	      process_ipa_clobber (fi, gimple_call_arg (t, 1));
+ 	      process_ipa_clobber (fi, gimple_call_arg (t, 2));
+ 	      return;
+ 	    }
+ 	  /* The following functions clobber their second argument.  */
+ 	  case BUILT_IN_FREXP:
+ 	  case BUILT_IN_FREXPF:
+ 	  case BUILT_IN_FREXPL:
+ 	  case BUILT_IN_LGAMMA_R:
+ 	  case BUILT_IN_LGAMMAF_R:
+ 	  case BUILT_IN_LGAMMAL_R:
+ 	  case BUILT_IN_GAMMA_R:
+ 	  case BUILT_IN_GAMMAF_R:
+ 	  case BUILT_IN_GAMMAL_R:
+ 	  case BUILT_IN_MODF:
+ 	  case BUILT_IN_MODFF:
+ 	  case BUILT_IN_MODFL:
+ 	    {
+ 	      process_ipa_clobber (fi, gimple_call_arg (t, 1));
+ 	      return;
+ 	    }
+ 	  /* The following functions clobber their third argument.  */
+ 	  case BUILT_IN_REMQUO:
+ 	  case BUILT_IN_REMQUOF:
+ 	  case BUILT_IN_REMQUOL:
+ 	    {
+ 	      process_ipa_clobber (fi, gimple_call_arg (t, 2));
+ 	      return;
+ 	    }
+ 	  /* The following functions neither read nor clobber memory.  */
+ 	  case BUILT_IN_FREE:
+ 	    return;
+ 	  /* Trampolines are of no interest to us.  */
+ 	  case BUILT_IN_INIT_TRAMPOLINE:
+ 	  case BUILT_IN_ADJUST_TRAMPOLINE:
+ 	    return;
+ 	  case BUILT_IN_VA_START:
+ 	  case BUILT_IN_VA_END:
+ 	    return;
+ 	  /* printf-style functions may have hooks to set pointers to
+ 	     point to somewhere into the generated string.  Leave them
+ 	     for a later excercise...  */
+ 	  default:
+ 	    /* Fallthru to general call handling.  */;
+ 	  }
+ 
+       /* Parameters passed by value are used.  */
+       lhs = get_function_part_constraint (fi, fi_uses);
+       for (i = 0; i < gimple_call_num_args (t); i++)
+ 	{
+ 	  struct constraint_expr *rhsp;
+ 	  tree arg = gimple_call_arg (t, i);
+ 
+ 	  if (TREE_CODE (arg) == SSA_NAME
+ 	      || is_gimple_min_invariant (arg))
+ 	    continue;
+ 
+ 	  get_constraint_for_address_of (arg, &rhsc);
+ 	  for (j = 0; VEC_iterate (ce_s, rhsc, j, rhsp); j++)
+ 	    process_constraint (new_constraint (lhs, *rhsp));
+ 	  VEC_free (ce_s, heap, rhsc);
+ 	}
+ 
+       /* Build constraints for propagating clobbers/uses along the
+ 	 callgraph edges.  */
+       cfi = get_fi_for_callee (t);
+       if (cfi->id == anything_id)
+ 	{
+ 	  if (gimple_vdef (t))
+ 	    make_constraint_from (first_vi_for_offset (fi, fi_clobbers),
+ 				  anything_id);
+ 	  make_constraint_from (first_vi_for_offset (fi, fi_uses),
+ 				anything_id);
+ 	  return;
+ 	}
+ 
+       /* For callees without function info (that's external functions),
+ 	 ESCAPED is clobbered and used.  */
+       if (gimple_call_fndecl (t)
+ 	  && !cfi->is_fn_info)
+ 	{
+ 	  varinfo_t vi;
+ 
+ 	  if (gimple_vdef (t))
+ 	    make_copy_constraint (first_vi_for_offset (fi, fi_clobbers),
+ 				  escaped_id);
+ 	  make_copy_constraint (first_vi_for_offset (fi, fi_uses), escaped_id);
+ 
+ 	  /* Also honor the call statement use/clobber info.  */
+ 	  if ((vi = lookup_call_clobber_vi (t)) != NULL)
+ 	    make_copy_constraint (first_vi_for_offset (fi, fi_clobbers),
+ 				  vi->id);
+ 	  if ((vi = lookup_call_use_vi (t)) != NULL)
+ 	    make_copy_constraint (first_vi_for_offset (fi, fi_uses),
+ 				  vi->id);
+ 	  return;
+ 	}
+ 
+       /* Otherwise the caller clobbers and uses what the callee does.
+ 	 ???  This should use a new complex constraint that filters
+ 	 local variables of the callee.  */
+       if (gimple_vdef (t))
+ 	{
+ 	  lhs = get_function_part_constraint (fi, fi_clobbers);
+ 	  rhs = get_function_part_constraint (cfi, fi_clobbers);
+ 	  process_constraint (new_constraint (lhs, rhs));
+ 	}
+       lhs = get_function_part_constraint (fi, fi_uses);
+       rhs = get_function_part_constraint (cfi, fi_uses);
+       process_constraint (new_constraint (lhs, rhs));
+     }
+   else if (gimple_code (t) == GIMPLE_ASM)
+     {
+       /* ???  Ick.  We can do better.  */
+       if (gimple_vdef (t))
+ 	make_constraint_from (first_vi_for_offset (fi, fi_clobbers),
+ 			      anything_id);
+       make_constraint_from (first_vi_for_offset (fi, fi_uses),
+ 			    anything_id);
+     }
+ 
+   VEC_free (ce_s, heap, rhsc);
+ }
+ 
+ 
  /* Find the first varinfo in the same variable as START that overlaps with
     OFFSET.  Return NULL if we can't find one.  */
  
*************** count_num_arguments (tree decl, bool *is
*** 4504,4538 ****
  static unsigned int
  create_function_info_for (tree decl, const char *name)
  {
    varinfo_t vi;
    tree arg;
    unsigned int i;
    bool is_varargs = false;
  
    /* Create the variable info.  */
  
    vi = new_var_info (decl, name);
    vi->offset = 0;
    vi->size = 1;
!   vi->fullsize = count_num_arguments (decl, &is_varargs) + 1;
    insert_vi_for_tree (vi->decl, vi);
  
    stats.total_vars++;
  
!   /* If it's varargs, we don't know how many arguments it has, so we
!      can't do much.  */
!   if (is_varargs)
      {
!       vi->fullsize = ~0;
!       vi->size = ~0;
!       vi->is_unknown_size_var = true;
!       return vi->id;
      }
  
!   arg = DECL_ARGUMENTS (decl);
  
    /* Set up variables for each argument.  */
!   for (i = 1; i < vi->fullsize; i++)
      {
        varinfo_t argvi;
        const char *newname;
--- 4981,5095 ----
  static unsigned int
  create_function_info_for (tree decl, const char *name)
  {
+   struct function *fn = DECL_STRUCT_FUNCTION (decl);
    varinfo_t vi;
    tree arg;
    unsigned int i;
    bool is_varargs = false;
+   unsigned int num_args = count_num_arguments (decl, &is_varargs);
  
    /* Create the variable info.  */
  
    vi = new_var_info (decl, name);
    vi->offset = 0;
    vi->size = 1;
!   vi->fullsize = fi_parm_base + num_args;
!   vi->is_fn_info = 1;
!   vi->may_have_pointers = false;
!   if (is_varargs)
!     vi->fullsize = ~0;
    insert_vi_for_tree (vi->decl, vi);
  
    stats.total_vars++;
  
!   /* Create a variable for things the function clobbers and one for
!      things the function uses.  */
      {
!       varinfo_t clobbervi, usevi;
!       const char *newname;
!       char *tempname;
! 
!       asprintf (&tempname, "%s.clobber", name);
!       newname = ggc_strdup (tempname);
!       free (tempname);
! 
!       clobbervi = new_var_info (NULL, newname);
!       clobbervi->offset = fi_clobbers;
!       clobbervi->size = 1;
!       clobbervi->fullsize = vi->fullsize;
!       clobbervi->is_full_var = true;
!       clobbervi->is_global_var = false;
!       insert_into_field_list_sorted (vi, clobbervi);
!       stats.total_vars++;
! 
!       asprintf (&tempname, "%s.use", name);
!       newname = ggc_strdup (tempname);
!       free (tempname);
! 
!       usevi = new_var_info (NULL, newname);
!       usevi->offset = fi_uses;
!       usevi->size = 1;
!       usevi->fullsize = vi->fullsize;
!       usevi->is_full_var = true;
!       usevi->is_global_var = false;
!       insert_into_field_list_sorted (vi, usevi);
!       stats.total_vars++;
      }
  
!   /* And one for the static chain.  */
!   if (fn->static_chain_decl != NULL_TREE)
!     {
!       varinfo_t chainvi;
!       const char *newname;
!       char *tempname;
! 
!       asprintf (&tempname, "%s.chain", name);
!       newname = ggc_strdup (tempname);
!       free (tempname);
! 
!       chainvi = new_var_info (fn->static_chain_decl, newname);
!       chainvi->offset = fi_static_chain;
!       chainvi->size = 1;
!       chainvi->fullsize = vi->fullsize;
!       chainvi->is_full_var = true;
!       chainvi->is_global_var = false;
!       insert_into_field_list_sorted (vi, chainvi);
!       stats.total_vars++;
!       insert_vi_for_tree (fn->static_chain_decl, chainvi);
!     }
! 
!   /* Create a variable for the return var.  */
!   if (DECL_RESULT (decl) != NULL
!       || !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl))))
!     {
!       varinfo_t resultvi;
!       const char *newname;
!       char *tempname;
!       tree resultdecl = decl;
! 
!       if (DECL_RESULT (decl))
! 	resultdecl = DECL_RESULT (decl);
! 
!       asprintf (&tempname, "%s.result", name);
!       newname = ggc_strdup (tempname);
!       free (tempname);
! 
!       resultvi = new_var_info (resultdecl, newname);
!       resultvi->offset = fi_result;
!       resultvi->size = 1;
!       resultvi->fullsize = vi->fullsize;
!       resultvi->is_full_var = true;
!       if (DECL_RESULT (decl))
! 	resultvi->may_have_pointers = could_have_pointers (DECL_RESULT (decl));
!       insert_into_field_list_sorted (vi, resultvi);
!       stats.total_vars++;
!       if (DECL_RESULT (decl))
! 	insert_vi_for_tree (DECL_RESULT (decl), resultvi);
!     }
  
    /* Set up variables for each argument.  */
!   arg = DECL_ARGUMENTS (decl);
!   for (i = 0; i < num_args; i++)
      {
        varinfo_t argvi;
        const char *newname;
*************** create_function_info_for (tree decl, con
*** 4542,4558 ****
        if (arg)
  	argdecl = arg;
  
!       asprintf (&tempname, "%s.arg%d", name, i-1);
        newname = ggc_strdup (tempname);
        free (tempname);
  
        argvi = new_var_info (argdecl, newname);
!       argvi->offset = i;
        argvi->size = 1;
        argvi->is_full_var = true;
        argvi->fullsize = vi->fullsize;
        insert_into_field_list_sorted (vi, argvi);
!       stats.total_vars ++;
        if (arg)
  	{
  	  insert_vi_for_tree (arg, argvi);
--- 5099,5117 ----
        if (arg)
  	argdecl = arg;
  
!       asprintf (&tempname, "%s.arg%d", name, i);
        newname = ggc_strdup (tempname);
        free (tempname);
  
        argvi = new_var_info (argdecl, newname);
!       argvi->offset = fi_parm_base + i;
        argvi->size = 1;
        argvi->is_full_var = true;
        argvi->fullsize = vi->fullsize;
+       if (arg)
+ 	argvi->may_have_pointers = could_have_pointers (arg);
        insert_into_field_list_sorted (vi, argvi);
!       stats.total_vars++;
        if (arg)
  	{
  	  insert_vi_for_tree (arg, argvi);
*************** create_function_info_for (tree decl, con
*** 4560,4592 ****
  	}
      }
  
!   /* Create a variable for the return var.  */
!   if (DECL_RESULT (decl) != NULL
!       || !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl))))
      {
!       varinfo_t resultvi;
        const char *newname;
        char *tempname;
!       tree resultdecl = decl;
! 
!       vi->fullsize ++;
  
!       if (DECL_RESULT (decl))
! 	resultdecl = DECL_RESULT (decl);
! 
!       asprintf (&tempname, "%s.result", name);
        newname = ggc_strdup (tempname);
        free (tempname);
  
!       resultvi = new_var_info (resultdecl, newname);
!       resultvi->offset = i;
!       resultvi->size = 1;
!       resultvi->fullsize = vi->fullsize;
!       resultvi->is_full_var = true;
!       insert_into_field_list_sorted (vi, resultvi);
!       stats.total_vars ++;
!       if (DECL_RESULT (decl))
! 	insert_vi_for_tree (DECL_RESULT (decl), resultvi);
      }
  
    return vi->id;
--- 5119,5148 ----
  	}
      }
  
!   /* Add one representative for all further args.  */
!   if (is_varargs)
      {
!       varinfo_t argvi;
        const char *newname;
        char *tempname;
!       tree decl;
  
!       asprintf (&tempname, "%s.varargs", name);
        newname = ggc_strdup (tempname);
        free (tempname);
  
!       /* We need sth that can be pointed to for va_start.  */
!       decl = create_tmp_var_raw (ptr_type_node, name);
!       get_var_ann (decl);
! 
!       argvi = new_var_info (decl, newname);
!       argvi->offset = fi_parm_base + num_args;
!       argvi->size = ~0;
!       argvi->is_full_var = true;
!       argvi->is_heap_var = true;
!       argvi->fullsize = vi->fullsize;
!       insert_into_field_list_sorted (vi, argvi);
!       stats.total_vars++;
      }
  
    return vi->id;
*************** create_variable_info_for (tree decl, con
*** 4647,4660 ****
      }
  
    insert_vi_for_tree (vi->decl, vi);
    if (vi->is_global_var
-       && (!flag_whole_program || !in_ipa_mode)
        && vi->may_have_pointers)
      {
        if (POINTER_TYPE_P (TREE_TYPE (decl))
  	  && TYPE_RESTRICT (TREE_TYPE (decl)))
  	make_constraint_from_restrict (vi, "GLOBAL_RESTRICT");
!       make_copy_constraint (vi, nonlocal_id);
      }
  
    stats.total_vars++;
--- 5203,5256 ----
      }
  
    insert_vi_for_tree (vi->decl, vi);
+ 
+   /* ???  The setting of vi->may_have_pointers is too conservative here
+      and may get refined below.  Thus we have superfluous constraints
+      here sometimes which triggers the commented assert in
+      dump_sa_points_to_info.  */
    if (vi->is_global_var
        && vi->may_have_pointers)
      {
+       /* Mark global restrict qualified pointers.  */
        if (POINTER_TYPE_P (TREE_TYPE (decl))
  	  && TYPE_RESTRICT (TREE_TYPE (decl)))
  	make_constraint_from_restrict (vi, "GLOBAL_RESTRICT");
! 
!       /* For escaped variables initialize them from nonlocal.  */
!       if (!in_ipa_mode
! 	  || DECL_EXTERNAL (decl) || TREE_PUBLIC (decl))
! 	make_copy_constraint (vi, nonlocal_id);
! 
!       /* If this is a global variable with an initializer and we are in
! 	 IPA mode generate constraints for it.  In non-IPA mode
! 	 the initializer from nonlocal is all we need.  */
!       if (in_ipa_mode
! 	  && DECL_INITIAL (vi->decl))
! 	{
! 	  VEC (ce_s, heap) *rhsc = NULL;
! 	  struct constraint_expr lhs, *rhsp;
! 	  unsigned i;
! 	  get_constraint_for (DECL_INITIAL (vi->decl), &rhsc);
! 	  lhs.var = vi->id;
! 	  lhs.offset = 0;
! 	  lhs.type = SCALAR;
! 	  for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); ++i)
! 	    process_constraint (new_constraint (lhs, *rhsp));
! 	  /* If this is a variable that escapes from the unit
! 	     the initializer escapes as well.  */
! 	  if (DECL_EXTERNAL (decl) || TREE_PUBLIC (decl))
! 	    {
! 	      lhs.var = escaped_id;
! 	      lhs.offset = 0;
! 	      lhs.type = SCALAR;
! 	      for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); ++i)
! 		process_constraint (new_constraint (lhs, *rhsp));
! 	    }
! 	  VEC_free (ce_s, heap, rhsc);
! 	  /* ???  Force us to not use subfields.  Else we'd have to parse
! 	     arbitrary initializers.  */
! 	  VEC_free (fieldoff_s, heap, fieldstack);
! 	}
      }
  
    stats.total_vars++;
*************** create_variable_info_for (tree decl, con
*** 4710,4716 ****
        vi->offset = fo->offset;
        vi->may_have_pointers = fo->may_have_pointers;
        if (vi->is_global_var
- 	  && (!flag_whole_program || !in_ipa_mode)
  	  && vi->may_have_pointers)
  	{
  	  if (fo->only_restrict_pointers)
--- 5306,5311 ----
*************** dump_solution_for_var (FILE *file, unsig
*** 4767,4786 ****
    unsigned int i;
    bitmap_iterator bi;
  
!   if (find (var) != var)
!     {
!       varinfo_t vipt = get_varinfo (find (var));
!       fprintf (file, "%s = same as %s\n", vi->name, vipt->name);
!     }
!   else
!     {
!       fprintf (file, "%s = { ", vi->name);
!       EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
! 	{
! 	  fprintf (file, "%s ", get_varinfo (i)->name);
! 	}
!       fprintf (file, "}\n");
!     }
  }
  
  /* Print the points-to solution for VAR to stdout.  */
--- 5362,5380 ----
    unsigned int i;
    bitmap_iterator bi;
  
!   /* Dump the solution for unified vars anyway, this avoids difficulties
!      in scanning dumps in the testsuite.  */
!   fprintf (file, "%s = { ", vi->name);
!   vi = get_varinfo (find (var));
!   EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
!     fprintf (file, "%s ", get_varinfo (i)->name);
!   fprintf (file, "}");
! 
!   /* But note when the variable was unified.  */
!   if (vi->id != var)
!     fprintf (file, " same as %s", vi->name);
! 
!   fprintf (file, "\n");
  }
  
  /* Print the points-to solution for VAR to stdout.  */
*************** set_uids_in_ptset (bitmap into, bitmap f
*** 4959,4967 ****
  	  || TREE_CODE (vi->decl) == PARM_DECL
  	  || TREE_CODE (vi->decl) == RESULT_DECL)
  	{
  	  /* Add the decl to the points-to set.  Note that the points-to
  	     set contains global variables.  */
! 	  bitmap_set_bit (into, DECL_UID (vi->decl));
  	  if (vi->is_global_var)
  	    pt->vars_contains_global = true;
  	}
--- 5553,5566 ----
  	  || TREE_CODE (vi->decl) == PARM_DECL
  	  || TREE_CODE (vi->decl) == RESULT_DECL)
  	{
+ 	  /* If we are in IPA mode we will not recompute points-to
+ 	     sets after inlining so make sure they stay valid.  */
+ 	  if (in_ipa_mode)
+ 	    SET_DECL_PT_UID (vi->decl, DECL_UID (vi->decl));
+ 
  	  /* Add the decl to the points-to set.  Note that the points-to
  	     set contains global variables.  */
! 	  bitmap_set_bit (into, DECL_PT_UID (vi->decl));
  	  if (vi->is_global_var)
  	    pt->vars_contains_global = true;
  	}
*************** find_what_var_points_to (varinfo_t vi, s
*** 4996,5002 ****
  	  if (vi->id == nothing_id)
  	    pt->null = 1;
  	  else if (vi->id == escaped_id)
! 	    pt->escaped = 1;
  	  else if (vi->id == nonlocal_id)
  	    pt->nonlocal = 1;
  	  else if (vi->is_heap_var)
--- 5595,5606 ----
  	  if (vi->id == nothing_id)
  	    pt->null = 1;
  	  else if (vi->id == escaped_id)
! 	    {
! 	      if (in_ipa_mode)
! 		pt->ipa_escaped = 1;
! 	      else
! 		pt->escaped = 1;
! 	    }
  	  else if (vi->id == nonlocal_id)
  	    pt->nonlocal = 1;
  	  else if (vi->is_heap_var)
*************** pt_solution_reset (struct pt_solution *p
*** 5102,5131 ****
  }
  
  /* Set the points-to solution *PT to point only to the variables
!    in VARS.  */
  
  void
! pt_solution_set (struct pt_solution *pt, bitmap vars)
  {
-   bitmap_iterator bi;
-   unsigned i;
- 
    memset (pt, 0, sizeof (struct pt_solution));
    pt->vars = vars;
!   EXECUTE_IF_SET_IN_BITMAP (vars, 0, i, bi)
      {
!       tree var = referenced_var_lookup (i);
!       if (is_global_var (var))
! 	{
! 	  pt->vars_contains_global = true;
! 	  break;
! 	}
      }
  }
  
  /* Return true if the points-to solution *PT is empty.  */
  
! static bool
  pt_solution_empty_p (struct pt_solution *pt)
  {
    if (pt->anything
--- 5706,5758 ----
  }
  
  /* Set the points-to solution *PT to point only to the variables
!    in VARS.  VARS_CONTAINS_GLOBAL specifies whether that contains
!    global variables and VARS_CONTAINS_RESTRICT specifies whether
!    it contains restrict tag variables.  */
  
  void
! pt_solution_set (struct pt_solution *pt, bitmap vars,
! 		 bool vars_contains_global, bool vars_contains_restrict)
  {
    memset (pt, 0, sizeof (struct pt_solution));
    pt->vars = vars;
!   pt->vars_contains_global = vars_contains_global;
!   pt->vars_contains_restrict = vars_contains_restrict;
! }
! 
! /* Computes the union of the points-to solutions *DEST and *SRC and
!    stores the result in *DEST.  This changes the points-to bitmap
!    of *DEST and thus may not be used if that might be shared.
!    The points-to bitmap of *SRC and *DEST will not be shared after
!    this function if they were not before.  */
! 
! static void
! pt_solution_ior_into (struct pt_solution *dest, struct pt_solution *src)
! {
!   dest->anything |= src->anything;
!   if (dest->anything)
      {
!       pt_solution_reset (dest);
!       return;
      }
+ 
+   dest->nonlocal |= src->nonlocal;
+   dest->escaped |= src->escaped;
+   dest->ipa_escaped |= src->ipa_escaped;
+   dest->null |= src->null;
+   dest->vars_contains_global |= src->vars_contains_global;
+   dest->vars_contains_restrict |= src->vars_contains_restrict;
+   if (!src->vars)
+     return;
+ 
+   if (!dest->vars)
+     dest->vars = BITMAP_GGC_ALLOC ();
+   bitmap_ior_into (dest->vars, src->vars);
  }
  
  /* Return true if the points-to solution *PT is empty.  */
  
! bool
  pt_solution_empty_p (struct pt_solution *pt)
  {
    if (pt->anything
*************** pt_solution_empty_p (struct pt_solution
*** 5141,5146 ****
--- 5768,5778 ----
        && !pt_solution_empty_p (&cfun->gimple_df->escaped))
      return false;
  
+   /* If the solution includes ESCAPED, check if that is empty.  */
+   if (pt->ipa_escaped
+       && !pt_solution_empty_p (&ipa_escaped_pt))
+     return false;
+ 
    return true;
  }
  
*************** pt_solution_includes_global (struct pt_s
*** 5157,5162 ****
--- 5789,5803 ----
    if (pt->escaped)
      return pt_solution_includes_global (&cfun->gimple_df->escaped);
  
+   if (pt->ipa_escaped)
+     return pt_solution_includes_global (&ipa_escaped_pt);
+ 
+   /* ???  This predicate is not correct for the IPA-PTA solution
+      as we do not properly distinguish between unit escape points
+      and global variables.  */
+   if (cfun->gimple_df->ipa_pta)
+     return true;
+ 
    return false;
  }
  
*************** pt_solution_includes_1 (struct pt_soluti
*** 5174,5180 ****
      return true;
  
    if (pt->vars
!       && bitmap_bit_p (pt->vars, DECL_UID (decl)))
      return true;
  
    /* If the solution includes ESCAPED, check it.  */
--- 5815,5821 ----
      return true;
  
    if (pt->vars
!       && bitmap_bit_p (pt->vars, DECL_PT_UID (decl)))
      return true;
  
    /* If the solution includes ESCAPED, check it.  */
*************** pt_solution_includes_1 (struct pt_soluti
*** 5182,5187 ****
--- 5823,5833 ----
        && pt_solution_includes_1 (&cfun->gimple_df->escaped, decl))
      return true;
  
+   /* If the solution includes ESCAPED, check it.  */
+   if (pt->ipa_escaped
+       && pt_solution_includes_1 (&ipa_escaped_pt, decl))
+     return true;
+ 
    return false;
  }
  
*************** pt_solutions_intersect_1 (struct pt_solu
*** 5232,5237 ****
--- 5878,5902 ----
  	return true;
      }
  
+   /* Check the escaped solution if required.
+      ???  Do we need to check the local against the IPA escaped sets?  */
+   if ((pt1->ipa_escaped || pt2->ipa_escaped)
+       && !pt_solution_empty_p (&ipa_escaped_pt))
+     {
+       /* If both point to escaped memory and that solution
+ 	 is not empty they alias.  */
+       if (pt1->ipa_escaped && pt2->ipa_escaped)
+ 	return true;
+ 
+       /* If either points to escaped memory see if the escaped solution
+ 	 intersects with the other.  */
+       if ((pt1->ipa_escaped
+ 	   && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2))
+ 	  || (pt2->ipa_escaped
+ 	      && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1)))
+ 	return true;
+     }
+ 
    /* Now both pointers alias if their points-to solution intersects.  */
    return (pt1->vars
  	  && pt2->vars
*************** dump_sa_points_to_info (FILE *outfile)
*** 5297,5303 ****
      }
  
    for (i = 0; i < VEC_length (varinfo_t, varmap); i++)
!     dump_solution_for_var (outfile, i);
  }
  
  
--- 5962,5979 ----
      }
  
    for (i = 0; i < VEC_length (varinfo_t, varmap); i++)
!     {
!       varinfo_t vi = get_varinfo (i);
!       if (!vi->may_have_pointers)
! 	{
! 	  gcc_assert (find (i) == i
! 		      || !(vi = get_varinfo (find (i)))->may_have_pointers);
! 	  /* ???  See create_variable_info_for.
! 	     gcc_assert (bitmap_empty_p (vi->solution));  */
! 	  continue;
! 	}
!       dump_solution_for_var (outfile, i);
!     }
  }
  
  
*************** solve_constraints (void)
*** 5570,5581 ****
    struct scc_info *si;
  
    if (dump_file)
-     {
-       fprintf (dump_file, "Points-to analysis\n\nConstraints:\n\n");
-       dump_constraints (dump_file);
-     }
- 
-   if (dump_file)
      fprintf (dump_file,
  	     "\nCollapsing static cycles and doing variable "
  	     "substitution\n");
--- 6246,6251 ----
*************** compute_points_to_sets (void)
*** 5643,5649 ****
  
    intra_create_variable_infos ();
  
!   /* Now walk all statements and derive aliases.  */
    FOR_EACH_BB (bb)
      {
        gimple_stmt_iterator gsi;
--- 6313,6319 ----
  
    intra_create_variable_infos ();
  
!   /* Now walk all statements and build the constraint set.  */
    FOR_EACH_BB (bb)
      {
        gimple_stmt_iterator gsi;
*************** compute_points_to_sets (void)
*** 5664,5669 ****
--- 6334,6345 ----
  	}
      }
  
+   if (dump_file)
+     {
+       fprintf (dump_file, "Points-to analysis\n\nConstraints:\n\n");
+       dump_constraints (dump_file, 0);
+     }
+ 
    /* From the constraints compute the points-to sets.  */
    solve_constraints ();
  
*************** delete_points_to_sets (void)
*** 5794,5799 ****
--- 6470,6492 ----
  unsigned int
  compute_may_aliases (void)
  {
+   if (cfun->gimple_df->ipa_pta)
+     {
+       if (dump_file)
+ 	{
+ 	  fprintf (dump_file, "\nNot re-computing points-to information "
+ 		   "because IPA points-to information is available.\n\n");
+ 
+ 	  /* But still dump what we have remaining it.  */
+ 	  dump_alias_info (dump_file);
+ 
+ 	  if (dump_flags & TDF_DETAILS)
+ 	    dump_referenced_vars (dump_file);
+ 	}
+ 
+       return 0;
+     }
+ 
    /* For each pointer P_i, determine the sets of variables that P_i may
       point-to.  Compute the reachability set of escaped and call-used
       variables.  */
*************** gate_ipa_pta (void)
*** 5878,5888 ****
--- 6571,6590 ----
  	  && !(errorcount || sorrycount));
  }
  
+ /* IPA PTA solutions for ESCAPED.
+    ???  We shouldn't need to compute CALLUSED or even generate constraints
+    for it.  OTOH CALLUSED represents ESCAPED but not possibly written-to
+    variables.  */
+ struct pt_solution ipa_escaped_pt
+   = { true, false, false, false, false, false, false, NULL };
+ 
  /* Execute the driver for IPA PTA.  */
  static unsigned int
  ipa_pta_execute (void)
  {
    struct cgraph_node *node;
+   struct varpool_node *var;
+   int from;
  
    in_ipa_mode = 1;
  
*************** ipa_pta_execute (void)
*** 5901,5916 ****
  	  || node->clone_of)
  	continue;
  
-       /* It does not make sense to have graph edges into or out of
-          externally visible functions.  There is no extra information
- 	 we can gather from them.  */
-       if (node->local.externally_visible)
- 	continue;
- 
        varid = create_function_info_for (node->decl,
  					cgraph_node_name (node));
      }
  
    for (node = cgraph_nodes; node; node = node->next)
      {
        struct function *func;
--- 6603,6625 ----
  	  || node->clone_of)
  	continue;
  
        varid = create_function_info_for (node->decl,
  					cgraph_node_name (node));
      }
  
+   /* Create constraints for global variables and their initializers.  */
+   for (var = varpool_nodes; var; var = var->next)
+     get_vi_for_tree (var->decl);
+ 
+   if (dump_file)
+     {
+       fprintf (dump_file,
+ 	       "Generating constraints for global initializers\n\n");
+       dump_constraints (dump_file, 0);
+       fprintf (dump_file, "\n");
+     }
+   from = VEC_length (constraint_t, constraints);
+ 
    for (node = cgraph_nodes; node; node = node->next)
      {
        struct function *func;
*************** ipa_pta_execute (void)
*** 5957,5972 ****
--- 6666,6872 ----
  	      gimple stmt = gsi_stmt (gsi);
  
  	      find_func_aliases (stmt);
+ 	      find_func_clobbers (stmt);
  	    }
  	}
  
        current_function_decl = old_func_decl;
        pop_cfun ();
+ 
+       if (dump_file)
+ 	{
+ 	  fprintf (dump_file, "\n");
+ 	  dump_constraints (dump_file, from);
+ 	  fprintf (dump_file, "\n");
+ 	}
+       from = VEC_length (constraint_t, constraints);
      }
  
    /* From the constraints compute the points-to sets.  */
    solve_constraints ();
  
+   /* Compute the global points-to sets for ESCAPED and CALLUSED.
+      ???  These cannot be used for call-clobber analysis.  Either
+      we do not need them at all or we need them to properly build
+      call-clobber information.
+      ???  Note that the computed escape set is also not correct
+      for the whole unit as we fail to consider graph edges to
+      externally visible functions.  */
+   find_what_var_points_to (get_varinfo (escaped_id), &ipa_escaped_pt);
+ 
+   /* Make sure the ESCAPED solution (which is used as placeholder in
+      other solutions) does not reference itself.  This simplifies
+      points-to solution queries.  */
+   ipa_escaped_pt.ipa_escaped = 0;
+ 
+   /* Assign the points-to sets to the SSA names in the unit.  */
+   for (node = cgraph_nodes; node; node = node->next)
+     {
+       tree ptr;
+       struct function *fn;
+       unsigned i;
+       varinfo_t fi;
+       basic_block bb;
+       struct pt_solution uses, clobbers;
+       struct cgraph_edge *e;
+ 
+       /* Nodes without a body are not interesting.  */
+       if (!gimple_has_body_p (node->decl)
+ 	  || node->clone_of)
+ 	continue;
+ 
+       fn = DECL_STRUCT_FUNCTION (node->decl);
+ 
+       /* Compute the points-to sets for pointer SSA_NAMEs.  */
+       for (i = 0; VEC_iterate (tree, fn->gimple_df->ssa_names, i, ptr); ++i)
+ 	{
+ 	  if (ptr
+ 	      && POINTER_TYPE_P (TREE_TYPE (ptr)))
+ 	    find_what_p_points_to (ptr);
+ 	}
+ 
+       /* Compute the call-use and call-clobber sets for all direct calls.  */
+       fi = lookup_vi_for_tree (node->decl);
+       gcc_assert (fi->is_fn_info);
+       find_what_var_points_to (first_vi_for_offset (fi, fi_clobbers),
+ 			       &clobbers);
+       find_what_var_points_to (first_vi_for_offset (fi, fi_uses), &uses);
+       for (e = node->callers; e; e = e->next_caller)
+ 	{
+ 	  if (!e->call_stmt)
+ 	    continue;
+ 
+ 	  *gimple_call_clobber_set (e->call_stmt) = clobbers;
+ 	  *gimple_call_use_set (e->call_stmt) = uses;
+ 	}
+ 
+       /* Compute the call-use and call-clobber sets for indirect calls
+ 	 and calls to external functions.  */
+       FOR_EACH_BB_FN (bb, fn)
+ 	{
+ 	  gimple_stmt_iterator gsi;
+ 
+ 	  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ 	    {
+ 	      gimple stmt = gsi_stmt (gsi);
+ 	      struct pt_solution *pt;
+ 	      varinfo_t vi;
+ 	      tree decl;
+ 
+ 	      if (!is_gimple_call (stmt))
+ 		continue;
+ 
+ 	      /* Handle direct calls to external functions.  */
+ 	      decl = gimple_call_fndecl (stmt);
+ 	      if (decl
+ 		  && (!(fi = lookup_vi_for_tree (decl))
+ 		      || !fi->is_fn_info))
+ 		{
+ 		  pt = gimple_call_use_set (stmt);
+ 		  if (gimple_call_flags (stmt) & ECF_CONST)
+ 		    memset (pt, 0, sizeof (struct pt_solution));
+ 		  else if ((vi = lookup_call_use_vi (stmt)) != NULL)
+ 		    {
+ 		      find_what_var_points_to (vi, pt);
+ 		      /* Escaped (and thus nonlocal) variables are always
+ 			 implicitly used by calls.  */
+ 		      /* ???  ESCAPED can be empty even though NONLOCAL
+ 			 always escaped.  */
+ 		      pt->nonlocal = 1;
+ 		      pt->ipa_escaped = 1;
+ 		    }
+ 		  else
+ 		    {
+ 		      /* If there is nothing special about this call then
+ 			 we have made everything that is used also escape.  */
+ 		      *pt = ipa_escaped_pt;
+ 		      pt->nonlocal = 1;
+ 		    }
+ 
+ 		  pt = gimple_call_clobber_set (stmt);
+ 		  if (gimple_call_flags (stmt) & (ECF_CONST|ECF_PURE|ECF_NOVOPS))
+ 		    memset (pt, 0, sizeof (struct pt_solution));
+ 		  else if ((vi = lookup_call_clobber_vi (stmt)) != NULL)
+ 		    {
+ 		      find_what_var_points_to (vi, pt);
+ 		      /* Escaped (and thus nonlocal) variables are always
+ 			 implicitly clobbered by calls.  */
+ 		      /* ???  ESCAPED can be empty even though NONLOCAL
+ 			 always escaped.  */
+ 		      pt->nonlocal = 1;
+ 		      pt->ipa_escaped = 1;
+ 		    }
+ 		  else
+ 		    {
+ 		      /* If there is nothing special about this call then
+ 			 we have made everything that is used also escape.  */
+ 		      *pt = ipa_escaped_pt;
+ 		      pt->nonlocal = 1;
+ 		    }
+ 		}
+ 
+ 	      /* Handle indirect calls.  */
+ 	      if (!decl
+ 		  && (fi = get_fi_for_callee (stmt)))
+ 		{
+ 		  /* We need to accumulate all clobbers/uses of all possible
+ 		     callees.  */
+ 		  fi = get_varinfo (find (fi->id));
+ 		  /* If we cannot constrain the set of functions we'll end up
+ 		     calling we end up using/clobbering everything.  */
+ 		  if (bitmap_bit_p (fi->solution, anything_id)
+ 		      || bitmap_bit_p (fi->solution, nonlocal_id)
+ 		      || bitmap_bit_p (fi->solution, escaped_id))
+ 		    {
+ 		      pt_solution_reset (gimple_call_clobber_set (stmt));
+ 		      pt_solution_reset (gimple_call_use_set (stmt));
+ 		    }
+ 		  else
+ 		    {
+ 		      bitmap_iterator bi;
+ 		      unsigned i;
+ 		      struct pt_solution *uses, *clobbers;
+ 
+ 		      uses = gimple_call_use_set (stmt);
+ 		      clobbers = gimple_call_clobber_set (stmt);
+ 		      memset (uses, 0, sizeof (struct pt_solution));
+ 		      memset (clobbers, 0, sizeof (struct pt_solution));
+ 		      EXECUTE_IF_SET_IN_BITMAP (fi->solution, 0, i, bi)
+ 			{
+ 			  struct pt_solution sol;
+ 
+ 			  vi = get_varinfo (i);
+ 			  if (!vi->is_fn_info)
+ 			    {
+ 			      /* ???  We could be more precise here?  */
+ 			      uses->nonlocal = 1;
+ 			      uses->ipa_escaped = 1;
+ 			      clobbers->nonlocal = 1;
+ 			      clobbers->ipa_escaped = 1;
+ 			      continue;
+ 			    }
+ 
+ 			  if (!uses->anything)
+ 			    {
+ 			      find_what_var_points_to
+ 				  (first_vi_for_offset (vi, fi_uses), &sol);
+ 			      pt_solution_ior_into (uses, &sol);
+ 			    }
+ 			  if (!clobbers->anything)
+ 			    {
+ 			      find_what_var_points_to
+ 				  (first_vi_for_offset (vi, fi_clobbers), &sol);
+ 			      pt_solution_ior_into (clobbers, &sol);
+ 			    }
+ 			}
+ 		    }
+ 		}
+ 	    }
+ 	}
+ 
+       fn->gimple_df->ipa_pta = true;
+     }
+ 
    delete_points_to_sets ();
  
    in_ipa_mode = 0;
Index: trunk/gcc/tree-into-ssa.c
===================================================================
*** trunk.orig/gcc/tree-into-ssa.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-into-ssa.c	2009-11-26 16:37:02.000000000 +0100
*************** dump_decl_set (FILE *file, bitmap set)
*** 1459,1465 ****
  
        EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
  	{
! 	  print_generic_expr (file, referenced_var (i), 0);
  	  fprintf (file, " ");
  	}
  
--- 1459,1473 ----
  
        EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
  	{
! 	  struct tree_decl_minimal in;
! 	  tree var;
! 	  in.uid = i;
! 	  var = (tree) htab_find_with_hash (gimple_referenced_vars (cfun),
! 					    &in, i);
! 	  if (var)
! 	    print_generic_expr (file, var, 0);
! 	  else
! 	    fprintf (file, "D.%u", i);
  	  fprintf (file, " ");
  	}
  
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,25 ----
+ /* { dg-do run } */
+ /* { dg-options "-O -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ static void __attribute__((noinline,noclone))
+ foo (int *p)
+ {
+   *p = 1;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   int i = 0;
+   foo (&i);
+   if (i != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* Verify we correctly compute the units ESCAPED set as empty but
+    still properly account for the store via *p in foo.  */
+ 
+ /* { dg-final { scan-ipa-dump "ESCAPED = { }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/tree-flow.h
===================================================================
*** trunk.orig/gcc/tree-flow.h	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-flow.h	2009-11-26 16:37:02.000000000 +0100
*************** struct GTY(()) gimple_df {
*** 80,85 ****
--- 80,88 ----
    /* True if the code is in ssa form.  */
    unsigned int in_ssa_p : 1;
  
+   /* True if IPA points-to information was computed for this function.  */
+   unsigned int ipa_pta : 1;
+ 
    struct ssa_operands ssa_operands;
  };
  
*************** typedef struct
*** 115,123 ****
  ---------------------------------------------------------------------------*/
  
  /* Aliasing information for SSA_NAMEs representing pointer variables.  */
  struct GTY(()) ptr_info_def
  {
!   /* The points-to solution, TBAA-pruned if the pointer is dereferenced.  */
    struct pt_solution pt;
  };
  
--- 118,127 ----
  ---------------------------------------------------------------------------*/
  
  /* Aliasing information for SSA_NAMEs representing pointer variables.  */
+ 
  struct GTY(()) ptr_info_def
  {
!   /* The points-to solution.  */
    struct pt_solution pt;
  };
  
Index: trunk/gcc/tree-ssa-alias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-alias.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-ssa-alias.c	2009-11-26 16:37:02.000000000 +0100
*************** ptr_deref_may_alias_decl_p (tree ptr, tr
*** 214,220 ****
    if (DECL_RESTRICTED_P (decl)
        && TYPE_RESTRICT (TREE_TYPE (ptr))
        && pi->pt.vars_contains_restrict)
!     return bitmap_bit_p (pi->pt.vars, DECL_UID (decl));
  
    return pt_solution_includes (&pi->pt, decl);
  }
--- 214,220 ----
    if (DECL_RESTRICTED_P (decl)
        && TYPE_RESTRICT (TREE_TYPE (ptr))
        && pi->pt.vars_contains_restrict)
!     return bitmap_bit_p (pi->pt.vars, DECL_PT_UID (decl));
  
    return pt_solution_includes (&pi->pt, decl);
  }
*************** dump_points_to_solution (FILE *file, str
*** 401,406 ****
--- 401,409 ----
    if (pt->escaped)
      fprintf (file, ", points-to escaped");
  
+   if (pt->ipa_escaped)
+     fprintf (file, ", points-to unit escaped");
+ 
    if (pt->null)
      fprintf (file, ", points-to NULL");
  
*************** dump_points_to_solution (FILE *file, str
*** 410,415 ****
--- 413,420 ----
        dump_decl_set (file, pt->vars);
        if (pt->vars_contains_global)
  	fprintf (file, " (includes global vars)");
+       if (pt->vars_contains_restrict)
+ 	fprintf (file, " (includes restrict tags)");
      }
  }
  
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,30 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fno-early-inlining -fipa-pta" } */
+ 
+ static void __attribute__((noinline,noclone))
+ clobber_me (int *p, int how)
+ {
+   *p = how;
+ }
+ 
+ /* When foo is inlined into main we have to make sure to adjust
+    main()s IPA CLOBBERED set according to the decl remappings
+    inlining does.  */
+ 
+ static int
+ foo (void)
+ {
+   int a = 0;
+   clobber_me (&a, 1);
+   return a;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   if (foo () != 1)
+     abort ();
+ 
+   return 0;
+ }
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,31 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fno-early-inlining -fipa-pta" } */
+ 
+ static int *__attribute__((noinline,noclone))
+ pass_me (int *p)
+ {
+   return p;
+ }
+ 
+ /* When foo is inlined into main we have to make sure to adjust
+    main()s IPA CLOBBERED set according to the decl remappings
+    inlining does.  */
+ 
+ static int
+ foo (void)
+ {
+   int a = 0;
+   int *p = pass_me (&a);
+   *p = 1;
+   return a;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   if (foo () != 1)
+     abort ();
+ 
+   return 0;
+ }
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,17 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta" } */
+ 
+ static void __attribute__((noinline,noclone))
+ foo (int *p, int *q)
+ {
+   __builtin_memcpy (p, q, sizeof (int));
+ }
+ extern void abort (void);
+ int main()
+ {
+   int i = 0, j = 1;
+   foo (&i, &j);
+   if (i != 1)
+     abort ();
+   return 0;
+ }
Index: trunk/gcc/tree-dfa.c
===================================================================
*** trunk.orig/gcc/tree-dfa.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-dfa.c	2009-11-26 16:37:02.000000000 +0100
*************** dump_variable (FILE *file, tree var)
*** 269,274 ****
--- 269,276 ----
    ann = var_ann (var);
  
    fprintf (file, ", UID D.%u", (unsigned) DECL_UID (var));
+   if (DECL_PT_UID (var) != DECL_UID (var))
+     fprintf (file, ", PT-UID D.%u", (unsigned) DECL_PT_UID (var));
  
    fprintf (file, ", ");
    print_generic_expr (file, TREE_TYPE (var), dump_flags);
Index: trunk/gcc/tree-inline.c
===================================================================
*** trunk.orig/gcc/tree-inline.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-inline.c	2009-11-26 16:37:02.000000000 +0100
*************** remap_ssa_name (tree name, copy_body_dat
*** 199,209 ****
--- 199,219 ----
        && (TREE_CODE (SSA_NAME_VAR (name)) != RESULT_DECL
  	  || !id->transform_return_to_modify))
      {
+       struct ptr_info_def *pi;
        new_tree = make_ssa_name (new_tree, NULL);
        insert_decl_map (id, name, new_tree);
        SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_tree)
  	= SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name);
        TREE_TYPE (new_tree) = TREE_TYPE (SSA_NAME_VAR (new_tree));
+       /* At least IPA points-to info can be directly transferred.  */
+       if (id->src_cfun->gimple_df
+ 	  && id->src_cfun->gimple_df->ipa_pta
+ 	  && (pi = SSA_NAME_PTR_INFO (name))
+ 	  && !pi->pt.anything)
+ 	{
+ 	  struct ptr_info_def *new_pi = get_ptr_info (new_tree);
+ 	  new_pi->pt = pi->pt;
+ 	}
        if (gimple_nop_p (SSA_NAME_DEF_STMT (name)))
  	{
  	  /* By inlining function having uninitialized variable, we might
*************** remap_gimple_stmt (gimple stmt, copy_bod
*** 1402,1413 ****
  		    break;
  		  }
  
! 	      /* Reset alias info.
! 	         ???  By maintaining DECL_PT_UID this should not
! 		 be necessary, but the plan is to only maintain
! 		 it when IPA-PTA was run.  It's not too easy to
! 		 detect this here ...  */
! 	      gimple_call_reset_alias_info (copy);
  	    }
  	    break;
  
--- 1412,1422 ----
  		    break;
  		  }
  
! 	      /* Reset alias info if we didn't apply measures to
! 		 keep it valid over inlining by setting DECL_PT_UID.  */
! 	      if (!id->src_cfun->gimple_df
! 		  || !id->src_cfun->gimple_df->ipa_pta)
! 		gimple_call_reset_alias_info (copy);
  	    }
  	    break;
  
*************** copy_decl_to_var (tree decl, copy_body_d
*** 4562,4567 ****
--- 4571,4578 ----
  
    copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
  		     VAR_DECL, DECL_NAME (decl), type);
+   if (DECL_PT_UID (decl) != DECL_UID (decl))
+     SET_DECL_PT_UID (copy, DECL_PT_UID (decl));
    TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
    TREE_READONLY (copy) = TREE_READONLY (decl);
    TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
*************** copy_result_decl_to_var (tree decl, copy
*** 4587,4592 ****
--- 4598,4605 ----
  
    copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
  		     VAR_DECL, DECL_NAME (decl), type);
+   if (DECL_PT_UID (decl) != DECL_UID (decl))
+     SET_DECL_PT_UID (copy, DECL_PT_UID (decl));
    TREE_READONLY (copy) = TREE_READONLY (decl);
    TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
    if (!DECL_BY_REFERENCE (decl))
Index: trunk/gcc/tree.c
===================================================================
*** trunk.orig/gcc/tree.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree.c	2009-11-26 16:37:02.000000000 +0100
*************** make_node_stat (enum tree_code code MEM_
*** 883,889 ****
        if (TREE_CODE (t) == DEBUG_EXPR_DECL)
  	DECL_UID (t) = --next_debug_decl_uid;
        else
! 	DECL_UID (t) = next_decl_uid++;
        if (TREE_CODE (t) == LABEL_DECL)
  	LABEL_DECL_UID (t) = -1;
  
--- 883,892 ----
        if (TREE_CODE (t) == DEBUG_EXPR_DECL)
  	DECL_UID (t) = --next_debug_decl_uid;
        else
! 	{
! 	  DECL_UID (t) = next_decl_uid++;
! 	  SET_DECL_PT_UID (t, -1);
! 	}
        if (TREE_CODE (t) == LABEL_DECL)
  	LABEL_DECL_UID (t) = -1;
  
*************** copy_node_stat (tree node MEM_STAT_DECL)
*** 963,969 ****
        if (code == DEBUG_EXPR_DECL)
  	DECL_UID (t) = --next_debug_decl_uid;
        else
! 	DECL_UID (t) = next_decl_uid++;
        if ((TREE_CODE (node) == PARM_DECL || TREE_CODE (node) == VAR_DECL)
  	  && DECL_HAS_VALUE_EXPR_P (node))
  	{
--- 966,976 ----
        if (code == DEBUG_EXPR_DECL)
  	DECL_UID (t) = --next_debug_decl_uid;
        else
! 	{
! 	  DECL_UID (t) = next_decl_uid++;
! 	  if (DECL_UID (node) != DECL_PT_UID (node))
! 	    SET_DECL_PT_UID (t, DECL_PT_UID (node));
! 	}
        if ((TREE_CODE (node) == PARM_DECL || TREE_CODE (node) == VAR_DECL)
  	  && DECL_HAS_VALUE_EXPR_P (node))
  	{
Index: trunk/gcc/tree.h
===================================================================
*** trunk.orig/gcc/tree.h	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree.h	2009-11-26 16:37:02.000000000 +0100
*************** struct function;
*** 2483,2488 ****
--- 2483,2494 ----
     uses.  */
  #define DEBUG_TEMP_UID(NODE) (-DECL_UID (TREE_CHECK ((NODE), DEBUG_EXPR_DECL)))
  
+ /* Every ..._DECL node gets a unique number that stays the same even
+    when the decl is copied by the inliner once it is set.  */
+ #define DECL_PT_UID(NODE) (DECL_MINIMAL_CHECK (NODE)->decl_minimal.pt_uid == -1u ? (NODE)->decl_minimal.uid : (NODE)->decl_minimal.pt_uid)
+ /* Initialize the ..._DECL node pt-uid to the decls uid.  */
+ #define SET_DECL_PT_UID(NODE, UID) (DECL_MINIMAL_CHECK (NODE)->decl_minimal.pt_uid = (UID))
+ 
  /* These two fields describe where in the source code the declaration
     was.  If the declaration appears in several places (as for a C
     function that is declared first and then defined later), this
*************** struct GTY(()) tree_decl_minimal {
*** 2506,2511 ****
--- 2512,2518 ----
    struct tree_common common;
    location_t locus;
    unsigned int uid;
+   unsigned int pt_uid;
    tree name;
    tree context;
  };
Index: trunk/gcc/tree-ssa-alias.h
===================================================================
*** trunk.orig/gcc/tree-ssa-alias.h	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-ssa-alias.h	2009-11-26 16:37:02.000000000 +0100
*************** struct GTY(()) pt_solution
*** 38,46 ****
       even if this is zero pt_vars can still include global variables.  */
    unsigned int nonlocal : 1;
  
!   /* Nonzero if the points-to set includes any escaped local variable.  */
    unsigned int escaped : 1;
  
    /* Nonzero if the points-to set includes 'nothing', the points-to set
       includes memory at address NULL.  */
    unsigned int null : 1;
--- 38,51 ----
       even if this is zero pt_vars can still include global variables.  */
    unsigned int nonlocal : 1;
  
!   /* Nonzero if the points-to set includes the local escaped solution by
!      reference.  */
    unsigned int escaped : 1;
  
+   /* Nonzero if the points-to set includes the IPA escaped solution by
+      reference.  */
+   unsigned int ipa_escaped : 1;
+ 
    /* Nonzero if the points-to set includes 'nothing', the points-to set
       includes memory at address NULL.  */
    unsigned int null : 1;
*************** extern void dump_alias_stats (FILE *);
*** 118,131 ****
  /* In tree-ssa-structalias.c  */
  extern unsigned int compute_may_aliases (void);
  extern void delete_alias_heapvars (void);
  extern bool pt_solution_includes_global (struct pt_solution *);
  extern bool pt_solution_includes (struct pt_solution *, const_tree);
  extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *);
  extern bool pt_solutions_same_restrict_base (struct pt_solution *,
  					     struct pt_solution *);
  extern void pt_solution_reset (struct pt_solution *);
! extern void pt_solution_set (struct pt_solution *, bitmap);
  extern void dump_pta_stats (FILE *);
  
  
  #endif /* TREE_SSA_ALIAS_H  */
--- 123,139 ----
  /* In tree-ssa-structalias.c  */
  extern unsigned int compute_may_aliases (void);
  extern void delete_alias_heapvars (void);
+ extern bool pt_solution_empty_p (struct pt_solution *);
  extern bool pt_solution_includes_global (struct pt_solution *);
  extern bool pt_solution_includes (struct pt_solution *, const_tree);
  extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *);
  extern bool pt_solutions_same_restrict_base (struct pt_solution *,
  					     struct pt_solution *);
  extern void pt_solution_reset (struct pt_solution *);
! extern void pt_solution_set (struct pt_solution *, bitmap, bool, bool);
  extern void dump_pta_stats (FILE *);
  
+ extern GTY(()) struct pt_solution ipa_escaped_pt;
+ 
  
  #endif /* TREE_SSA_ALIAS_H  */
Index: trunk/gcc/cfgexpand.c
===================================================================
*** trunk.orig/gcc/cfgexpand.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/cfgexpand.c	2009-11-26 16:37:02.000000000 +0100
*************** update_alias_info_with_stack_vars (void)
*** 555,566 ****
        for (j = i; j != EOC; j = stack_vars[j].next)
  	{
  	  tree decl = stack_vars[j].decl;
! 	  unsigned int uid = DECL_UID (decl);
  	  /* We should never end up partitioning SSA names (though they
  	     may end up on the stack).  Neither should we allocate stack
  	     space to something that is unused and thus unreferenced.  */
  	  gcc_assert (DECL_P (decl)
! 		      && referenced_var_lookup (uid));
  	  bitmap_set_bit (part, uid);
  	  *((bitmap *) pointer_map_insert (decls_to_partitions,
  					   (void *)(size_t) uid)) = part;
--- 555,566 ----
        for (j = i; j != EOC; j = stack_vars[j].next)
  	{
  	  tree decl = stack_vars[j].decl;
! 	  unsigned int uid = DECL_PT_UID (decl);
  	  /* We should never end up partitioning SSA names (though they
  	     may end up on the stack).  Neither should we allocate stack
  	     space to something that is unused and thus unreferenced.  */
  	  gcc_assert (DECL_P (decl)
! 		      && referenced_var_lookup (DECL_UID (decl)));
  	  bitmap_set_bit (part, uid);
  	  *((bitmap *) pointer_map_insert (decls_to_partitions,
  					   (void *)(size_t) uid)) = part;
*************** update_alias_info_with_stack_vars (void)
*** 570,576 ****
  
        /* Make the SSA name point to all partition members.  */
        pi = get_ptr_info (name);
!       pt_solution_set (&pi->pt, part);
      }
  
    /* Make all points-to sets that contain one member of a partition
--- 570,576 ----
  
        /* Make the SSA name point to all partition members.  */
        pi = get_ptr_info (name);
!       pt_solution_set (&pi->pt, part, false, false);
      }
  
    /* Make all points-to sets that contain one member of a partition
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,30 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ #include <stdarg.h>
+ 
+ static void __attribute__((noinline,noclone))
+ foo (int i, ...)
+ {
+   va_list ap;
+   int *p;
+   va_start (ap, i);
+   p = va_arg (ap, int *);
+   *p = 1;
+   va_end (ap);
+ }
+ extern void abort (void);
+ int main()
+ {
+   int i = 0;
+   foo (0, &i);
+   if (i != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* Verify we properly handle variadic arguments and do not let escape
+    stuff through it.  */
+ 
+ /* { dg-final { scan-ipa-dump "ESCAPED = { }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-11.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-11.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,33 ----
+ /* { dg-do link } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ static int i;
+ /* i should not escape here, p should point to i only.  */
+ /* { dg-final { scan-ipa-dump "p = { i }" "pta" } } */
+ static int *p = &i;
+ 
+ int j;
+ /* q should point to j only.  */
+ /* { dg-final { scan-ipa-dump "q = { j }" "pta" } } */
+ static int *q = &j;
+ 
+ static int k;
+ /* k should escape here, r should point to NONLOCAL, ESCAPED, k.  */
+ int *r = &k;
+ /* { dg-final { scan-ipa-dump "r = { ESCAPED NONLOCAL k }" "pta" } } */
+ 
+ int l;
+ /* s should point to NONLOCAL, ESCAPED, l.  */
+ int *s = &l;
+ /* { dg-final { scan-ipa-dump "s = { ESCAPED NONLOCAL l }" "pta" } } */
+ 
+ int main()
+ {
+   return 0;
+ }
+ 
+ /* It isn't clear if the escape if l is strictly necessary, if it were
+    we should have i, r and s in ESCAPED as well.  */
+ 
+ /* { dg-final { scan-ipa-dump "ESCAPED = { ESCAPED NONLOCAL l k }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-12.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-12.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,34 ----
+ static int i, j;
+ 
+ static void __attribute__((noinline,noclone))
+ foo (void) { i = 1; }
+ 
+ static void __attribute__((noinline,noclone))
+ bar (void) { j = 1; }
+ 
+ typedef void (*fn_t)(void);
+ void escapeme (fn_t);
+ fn_t getme (void);
+ 
+ extern void link_error (void);
+ 
+ int main()
+ {
+   fn_t fn;
+   escapeme (foo);
+   fn = getme();
+ 
+   i = 0;
+   fn();
+   if (i != 1)
+     return 100;
+   j = 0;
+   fn();
+   if (j != 0)
+     link_error ();
+   bar();
+   if (j != 1)
+     return 200;
+ 
+   return 0;
+ }
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c	2009-11-26 16:37:02.000000000 +0100
***************
*** 0 ****
--- 1,56 ----
+ /* { dg-do link } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre" } */
+ 
+ static int x, y;
+ 
+ static __attribute__((noinline,noclone)) void
+ local (int *p)
+ {
+   *p = 1;
+ }
+ 
+ static __attribute__((noinline,noclone)) void
+ local_address_taken (int *p)
+ {
+   *p = 1;
+ }
+ 
+ /* Even though not referenced in this TU we should have added constraints
+    for the initializer.  */
+ /* { dg-final { scan-ipa-dump "ex = &local_address_taken" "pta" } } */
+ void (*ex)(int *) = local_address_taken;
+ 
+ extern void link_error (void);
+ 
+ int main()
+ {
+   void (*anyfn)(int *) = (void (*)(int *))(__SIZE_TYPE__)x;
+   /* The following should cause local_address_taken to get &x
+      as argument, but not local.  We shouldn't get &x added to
+      arbitrary special sub-vars of local_address_taken though,
+      a missed optimization currently.
+      As local_address_taken escapes the translation unit its
+      argument points-to set needs to include ESCAPED and NONLOCAL.
+      We shouldn't get the functions sub-vars in the ESCAPED solution
+      though, another missed-optimization.  This also causes the functions
+      uses to be messed up even further.  */
+   /* { dg-final { scan-ipa-dump "local_address_taken.arg0 = { ESCAPED NONLOCAL y x }" "pta" } } */
+   /* { dg-final { scan-ipa-dump "local_address_taken.clobber = { ESCAPED NONLOCAL y x }" "pta" } } */
+   /* { dg-final { scan-ipa-dump "local_address_taken.use = { }" "pta" { xfail *-*-* } } } */
+   (*anyfn) (&x);
+   x = 0;
+   local (&y);
+   /* Thus we should be able to disambiguate x against the call to local
+      and CSE the stored value.  */
+   if (x != 0)
+     link_error ();
+   x = 1;
+   local_address_taken (&y);
+   /* As we are computing flow- and context-insensitive we may not
+      CSE the load of x here.  */
+   /* { dg-final { scan-tree-dump " = x;" "fre" } } */
+   return x;
+ }
+ 
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
+ /* { dg-final { cleanup-tree-dump "fre" } } */
Index: trunk/gcc/gimple-pretty-print.c
===================================================================
*** trunk.orig/gcc/gimple-pretty-print.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/gimple-pretty-print.c	2009-11-26 16:37:02.000000000 +0100
*************** dump_gimple_call_args (pretty_printer *b
*** 477,482 ****
--- 477,529 ----
      }
  }
  
+ /* Dump the points-to solution *PT to BUFFER.  */
+ 
+ static void
+ pp_points_to_solution (pretty_printer *buffer, struct pt_solution *pt)
+ {
+   if (pt->anything)
+     {
+       pp_string (buffer, "anything ");
+       return;
+     }
+   if (pt->nonlocal)
+     pp_string (buffer, "nonlocal ");
+   if (pt->escaped)
+     pp_string (buffer, "escaped ");
+   if (pt->ipa_escaped)
+     pp_string (buffer, "unit-escaped ");
+   if (pt->null)
+     pp_string (buffer, "null ");
+   if (pt->vars
+       && !bitmap_empty_p (pt->vars))
+     {
+       bitmap_iterator bi;
+       unsigned i;
+       pp_string (buffer, "{ ");
+       EXECUTE_IF_SET_IN_BITMAP (pt->vars, 0, i, bi)
+ 	{
+ 	  struct tree_decl_minimal in;
+ 	  tree var;
+ 	  in.uid = i;
+ 	  var = (tree) htab_find_with_hash (gimple_referenced_vars (cfun),
+ 					    &in, i);
+ 	  if (var)
+ 	    dump_generic_node (buffer, var, 0, 0, false);
+ 	  else
+ 	    {
+ 	      pp_string (buffer, "D.");
+ 	      pp_decimal_int (buffer, i);
+ 	    }
+ 	  pp_character (buffer, ' ');
+ 	}
+       pp_character (buffer, '}');
+       if (pt->vars_contains_global)
+ 	pp_string (buffer, " (glob)");
+       if (pt->vars_contains_restrict)
+ 	pp_string (buffer, " (restr)");
+     }
+ }
  
  /* Dump the call statement GS.  BUFFER, SPC and FLAGS are as in
     dump_gimple_stmt.  */
*************** dump_gimple_call (pretty_printer *buffer
*** 486,491 ****
--- 533,557 ----
  {
    tree lhs = gimple_call_lhs (gs);
  
+   if (flags & TDF_ALIAS)
+     {
+       struct pt_solution *pt;
+       pt = gimple_call_use_set (gs);
+       if (!pt_solution_empty_p (pt))
+ 	{
+ 	  pp_string (buffer, "# USE = ");
+ 	  pp_points_to_solution (buffer, pt);
+ 	  newline_and_indent (buffer, spc);
+ 	}
+       pt = gimple_call_clobber_set (gs);
+       if (!pt_solution_empty_p (pt))
+ 	{
+ 	  pp_string (buffer, "# CLB = ");
+ 	  pp_points_to_solution (buffer, pt);
+ 	  newline_and_indent (buffer, spc);
+ 	}
+     }
+ 
    if (flags & TDF_RAW)
      {
        dump_gimple_fmt (buffer, spc, flags, "%G <%T, %T",
Index: trunk/gcc/tree-dump.c
===================================================================
*** trunk.orig/gcc/tree-dump.c	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-dump.c	2009-11-26 16:37:02.000000000 +0100
*************** static const struct dump_option_value_in
*** 821,826 ****
--- 821,827 ----
    {"memsyms", TDF_MEMSYMS},
    {"verbose", TDF_VERBOSE},
    {"eh", TDF_EH},
+   {"alias", TDF_ALIAS},
    {"all", ~(TDF_RAW | TDF_SLIM | TDF_LINENO | TDF_TREE | TDF_RTL | TDF_IPA
  	    | TDF_STMTADDR | TDF_GRAPH | TDF_DIAGNOSTIC | TDF_VERBOSE
  	    | TDF_RHS_ONLY)},
Index: trunk/gcc/tree-pass.h
===================================================================
*** trunk.orig/gcc/tree-pass.h	2009-11-26 16:36:40.000000000 +0100
--- trunk/gcc/tree-pass.h	2009-11-26 16:37:02.000000000 +0100
*************** enum tree_dump_index
*** 78,83 ****
--- 78,84 ----
  #define TDF_ASMNAME	(1 << 18)	/* display asm names of decls  */
  #define TDF_EH		(1 << 19)	/* display EH region number
  					   holding this gimple statement.  */
+ #define TDF_ALIAS	(1 << 20)	/* display alias information  */
  
  
  /* In tree-dump.c */
Index: trunk/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
===================================================================
*** trunk.orig/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c	2009-10-27 16:26:16.000000000 +0100
--- trunk/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c	2009-11-26 16:41:03.000000000 +0100
*************** void test4 (int a4, char b, char c, char
*** 42,46 ****
    bar (p);
  }
  
! /* { dg-final { scan-ipa-dump "bar.arg0 = { a4 a3 a2 a1 }" "pta" } } */
  /* { dg-final { cleanup-ipa-dump "pta" } } */
--- 42,46 ----
    bar (p);
  }
  
! /* { dg-final { scan-ipa-dump "bar.arg0 = { test4.arg0 test3.arg0 test2.arg0 test1.arg0 }" "pta" } } */
  /* { dg-final { cleanup-ipa-dump "pta" } } */

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2009-11-26 16:25 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-26 16:42 [PATCH][RFC] Implement IPA points-to and call mod/ref analysis Richard Guenther

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).