public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Fix IPA-PTA, implement IPA mod/ref analysis
@ 2010-04-14 14:55 Richard Guenther
  2010-04-15 22:22 ` Steven Bosscher
  2011-01-24 11:42 ` H.J. Lu
  0 siblings, 2 replies; 5+ messages in thread
From: Richard Guenther @ 2010-04-14 14:55 UTC (permalink / raw)
  To: gcc-patches; +Cc: Diego Novillo


This patch fixes up IPA-PTA to do a reasonable and correct job as
well as put the results somewhere so they can be used.  Alongside
PTA we do mod/ref analysis.

The results of mod/ref analysis are stored in the now separate
points-to sets for uses and clobbers in call statements.  The
points-to results for SSA names are stored in the same place
as the local PTA results.  This requires us to not re-compute
local PTA information once IPA-PTA was performed.

As IPA-PTA runs before inlining decisions are committed we
1) have a more context-insensitive view than necessary,
2) we have to keep the points-to sets valid over the declaration
copying inlining performs.   For the latter this patch introduces
a new uid, DECL_PT_UID, that is stable over decl copies - that
keeps the points-to sets small and avoids the need to touch
all points-to sets in the program when copying a single decl
(well - I wouldn't know how to do that efficiently anyway without
a back-mapping of decl to points-to sets where it is set).
Clever ideas welcome, but I don't think there are any - DECL_PT_UID
is the most clever thing I could come up with.

The patch adds a -alias dump flag which for now prints the call
use/clobber sets like

  # USE = nonlocal null { D.1543 D.1546 D.1550 D.1565 cD.1566 dtD.1570 
D.1576 options.4D.1582 D.1642 D.1643 } (glob) (restr)
  # CLB = nonlocal null { D.1543 D.1546 D.1550 D.1565 cD.1566 dtD.1570 
D.1576 options.4D.1582 D.1642 D.1643 } (glob) (restr)
  D.1655_15 = dtne (&dtD.1570, &D.1652);

and prints DECL_PT_UID where relevant alongside the regular UIDs

  mytype.3D.1651.ptD.1567 = 4.2e+1;

and in future when I come around to adding it print points-to
sets of SSA names at their point of definition.

The patch has been bootstrapped and tested on x86_64-unknown-linux-gnu.
I have also run the testsuite with -fipa-pta enabled with some
expected spurious tree-scan FAILs and

FAIL: c-c++-common/restrict-4.c scan-tree-dump lim1 "Executing store 
motion"
FAIL: gcc.dg/tree-ssa/ssa-dse-1.c scan-tree-dump-times dse1 " = 4" 0
FAIL: gfortran.dg/whole_file_3.f90  -O  (internal compiler error)
FAIL: gfortran.dg/whole_file_3.f90  -O  (test for excess errors)
FAIL: gfortran.dg/vect/vect-gems.f90  -O  scan-tree-dump-times vect 
"vectorized 1 loops" 1
FAIL: 20_util/shared_ptr/thread/default_weaktoshared.cc execution test
FAIL: 23_containers/bitset/cons/1.cc execution test
FAIL: 23_containers/bitset/operations/1.cc execution test
FAIL: 23_containers/bitset/to_ullong/1.cc execution test
WARNING: program timed out.
WARNING: program timed out.
FAIL: tr1/2_general_utilities/shared_ptr/thread/default_weaktoshared.cc 
executio
n test

In the past when I implemented all this I could build all of
SPEC2006 CPU INT with -flto -fipa-pta apart from 403.gcc
and 483.xalancbmk where it did blow up my machine (PTA can be
quite costly).  As IPA-PTA isn't enabled by default the changes
there should be safe (though there for sure are bugs).

If there are no comments I'll go ahead with this tomorrow.

Thanks,
Richard.

2010-04-14  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.
	(insert_into_field_list_sorted): Remove.
	(create_function_info_for): Properly allocate vars for IPA mode.
	Do not use insert_into_field_list_sorted.
	(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.
	(DECL_PT_UID_SET_P): 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.
	* doc/invoke.texi (-fipa-pta): Update documentation.

	* 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-ssa-structalias.c	2010-04-14 15:08:16.000000000 +0200
***************
*** 161,166 ****
--- 161,208 ----
    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
*** 236,241 ****
--- 278,286 ----
    /* 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)
*** 368,373 ****
--- 413,419 ----
    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)
*** 677,687 ****
  /* 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);
  }
  
--- 723,733 ----
  /* 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)
*** 690,696 ****
  void
  debug_constraints (void)
  {
!   dump_constraints (stderr);
  }
  
  /* Print out to FILE the edge in the constraint graph that is created by
--- 736,742 ----
  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)
*** 741,747 ****
    /* 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:  */
--- 787,793 ----
    /* 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_constraint_from_heapvar (varinfo_t
*** 3506,3512 ****
  
    /* 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);
--- 3552,3558 ----
  
    /* 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
*** 3539,3544 ****
--- 3585,3632 ----
    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).  */
+ 
+ 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,
*** 3728,3733 ****
--- 3816,3855 ----
    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)
*** 3740,3745 ****
--- 3862,3868 ----
    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)
*** 3892,3897 ****
--- 4015,4102 ----
  	  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)
*** 3900,3906 ****
  	  }
        if (!in_ipa_mode
  	  || (fndecl
! 	      && !lookup_vi_for_tree (fndecl)))
  	{
  	  VEC(ce_s, heap) *rhsc = NULL;
  	  int flags = gimple_call_flags (t);
--- 4105,4112 ----
  	  }
        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)
*** 3928,3951 ****
        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.  */
--- 4134,4142 ----
        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)
*** 3955,4005 ****
  	      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
--- 4146,4215 ----
  	      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);
! 	      if (fndecl
! 		  && DECL_RESULT (fndecl)
! 		  && DECL_BY_REFERENCE (DECL_RESULT (fndecl)))
  		{
! 		  VEC(ce_s, heap) *tem = NULL;
! 		  VEC_safe_push (ce_s, heap, tem, &rhs);
! 		  do_deref (&tem);
! 		  rhs = *VEC_index (ce_s, tem, 0);
! 		  VEC_free(ce_s, heap, tem);
  		}
  	      for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
  		process_constraint (new_constraint (*lhsp, rhs));
  	    }
+ 
+ 	  /* If we pass the result decl by reference, honor that.  */
+ 	  if (lhsop
+ 	      && fndecl
+ 	      && DECL_RESULT (fndecl)
+ 	      && DECL_BY_REFERENCE (DECL_RESULT (fndecl)))
+ 	    {
+ 	      struct constraint_expr lhs;
+ 	      struct constraint_expr *rhsp;
+ 
+ 	      get_constraint_for_address_of (lhsop, &rhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_result);
+ 	      for (j = 0; VEC_iterate (ce_s, rhsc, j, rhsp); j++)
+ 		process_constraint (new_constraint (lhs, *rhsp));
+ 	      VEC_free (ce_s, heap, rhsc);
+ 	    }
+ 
+ 	  /* 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)
*** 4039,4045 ****
        /* If there is a store to a global variable the rhs escapes.  */
        if ((lhsop = get_base_address (lhsop)) != NULL_TREE
  	  && DECL_P (lhsop)
! 	  && is_global_var (lhsop))
  	make_escape_constraint (rhsop);
        /* If this is a conversion of a non-restrict pointer to a
  	 restrict pointer track it with a new heapvar.  */
--- 4249,4257 ----
        /* If there is a store to a global variable the rhs escapes.  */
        if ((lhsop = get_base_address (lhsop)) != NULL_TREE
  	  && DECL_P (lhsop)
! 	  && is_global_var (lhsop)
! 	  && (!in_ipa_mode
! 	      || DECL_EXTERNAL (lhsop) || TREE_PUBLIC (lhsop)))
  	make_escape_constraint (rhsop);
        /* If this is a conversion of a non-restrict pointer to a
  	 restrict pointer track it with a new heapvar.  */
*************** find_func_aliases (gimple origt)
*** 4063,4069 ****
  	   && 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)
--- 4275,4296 ----
  	   && 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)
*** 4132,4137 ****
--- 4359,4641 ----
  }
  
  
+ /* 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.  */
  
*************** insert_into_field_list (varinfo_t base,
*** 4204,4237 ****
    prev->next = field;
  }
  
- /* Insert the varinfo FIELD into the field list for BASE, ordered by
-    offset.  */
- 
- static void
- insert_into_field_list_sorted (varinfo_t base, varinfo_t field)
- {
-   varinfo_t prev = base;
-   varinfo_t curr = base->next;
- 
-   if (curr == NULL)
-     {
-       prev->next = field;
-       field->next = NULL;
-     }
-   else
-     {
-       while (curr)
- 	{
- 	  if (field->offset <= curr->offset)
- 	    break;
- 	  prev = curr;
- 	  curr = curr->next;
- 	}
-       field->next = prev->next;
-       prev->next = field;
-     }
- }
- 
  /* This structure is used during pushing fields onto the fieldstack
     to track the offset of the field, since bitpos_of_field gives it
     relative to its immediate containing type, and we want it relative
--- 4708,4713 ----
*************** count_num_arguments (tree decl, bool *is
*** 4432,4466 ****
  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;
--- 4908,5032 ----
  static unsigned int
  create_function_info_for (tree decl, const char *name)
  {
!   struct function *fn = DECL_STRUCT_FUNCTION (decl);
!   varinfo_t vi, prev_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++;
  
!   prev_vi = vi;
! 
!   /* 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;
!       gcc_assert (prev_vi->offset < clobbervi->offset);
!       prev_vi->next = clobbervi;
!       prev_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;
!       gcc_assert (prev_vi->offset < usevi->offset);
!       prev_vi->next = usevi;
!       prev_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;
!       gcc_assert (prev_vi->offset < chainvi->offset);
!       prev_vi->next = chainvi;
!       prev_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));
!       gcc_assert (prev_vi->offset < resultvi->offset);
!       prev_vi->next = resultvi;
!       prev_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
*** 4470,4486 ****
        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);
--- 5036,5056 ----
        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);
!       gcc_assert (prev_vi->offset < argvi->offset);
!       prev_vi->next = argvi;
!       prev_vi = argvi;
!       stats.total_vars++;
        if (arg)
  	{
  	  insert_vi_for_tree (arg, argvi);
*************** create_function_info_for (tree decl, con
*** 4488,4520 ****
  	}
      }
  
!   /* 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;
--- 5058,5089 ----
  	}
      }
  
!   /* 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;
!       gcc_assert (prev_vi->offset < argvi->offset);
!       prev_vi->next = argvi;
!       prev_vi = argvi;
!       stats.total_vars++;
      }
  
    return vi->id;
*************** create_variable_info_for (tree decl, con
*** 4575,4588 ****
      }
  
    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++;
--- 5144,5197 ----
      }
  
    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
*** 4638,4644 ****
        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)
--- 5247,5252 ----
*************** dump_solution_for_var (FILE *file, unsig
*** 4695,4714 ****
    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.  */
--- 5303,5321 ----
    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
*** 4888,4896 ****
  	  || 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;
  	}
--- 5495,5509 ----
  	  || 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
+ 	      && !DECL_PT_UID_SET_P (vi->decl))
+ 	    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 orig_
*** 4926,4932 ****
  	  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)
--- 5539,5550 ----
  	  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
*** 5032,5061 ****
  }
  
  /* 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
--- 5650,5702 ----
  }
  
  /* 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
*** 5071,5076 ****
--- 5712,5722 ----
        && !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
*** 5087,5092 ****
--- 5733,5747 ----
    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
*** 5104,5110 ****
      return true;
  
    if (pt->vars
!       && bitmap_bit_p (pt->vars, DECL_UID (decl)))
      return true;
  
    /* If the solution includes ESCAPED, check it.  */
--- 5759,5765 ----
      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
*** 5112,5117 ****
--- 5767,5777 ----
        && 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
*** 5162,5167 ****
--- 5822,5846 ----
  	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)
*** 5227,5233 ****
      }
  
    for (i = 0; i < VEC_length (varinfo_t, varmap); i++)
!     dump_solution_for_var (outfile, i);
  }
  
  
--- 5906,5923 ----
      }
  
    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)
*** 5500,5511 ****
    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");
--- 6190,6195 ----
*************** compute_points_to_sets (void)
*** 5573,5579 ****
  
    intra_create_variable_infos ();
  
!   /* Now walk all statements and derive aliases.  */
    FOR_EACH_BB (bb)
      {
        gimple_stmt_iterator gsi;
--- 6257,6263 ----
  
    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)
*** 5594,5599 ****
--- 6278,6289 ----
  	}
      }
  
+   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)
*** 5724,5729 ****
--- 6414,6436 ----
  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)
*** 5808,5818 ****
--- 6515,6531 ----
  	  && !(errorcount || sorrycount));
  }
  
+ /* IPA PTA solutions for ESCAPED.  */
+ 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)
*** 5829,5844 ****
  	  || 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;
- 
        create_function_info_for (node->decl,
  				cgraph_node_name (node));
      }
  
    for (node = cgraph_nodes; node; node = node->next)
      {
        struct function *func;
--- 6542,6564 ----
  	  || node->clone_of)
  	continue;
  
        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)
*** 5885,5900 ****
--- 6605,6808 ----
  	      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.
+      ???  Note that the computed escape set is 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-into-ssa.c	2010-04-14 14:58:18.000000000 +0200
*************** dump_decl_set (FILE *file, bitmap set)
*** 1475,1481 ****
  
        EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
  	{
! 	  print_generic_expr (file, referenced_var (i), 0);
  	  fprintf (file, " ");
  	}
  
--- 1475,1489 ----
  
        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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-flow.h	2010-04-14 14:58:18.000000000 +0200
*************** struct GTY(()) gimple_df {
*** 76,81 ****
--- 76,84 ----
    /* 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
*** 111,119 ****
  ---------------------------------------------------------------------------*/
  
  /* 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;
  };
  
--- 114,123 ----
  ---------------------------------------------------------------------------*/
  
  /* 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-ssa-alias.c	2010-04-14 14:58:18.000000000 +0200
*************** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-dfa.c	2010-04-14 14:58:18.000000000 +0200
*************** 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-inline.c	2010-04-14 14:58:18.000000000 +0200
*************** remap_ssa_name (tree name, copy_body_dat
*** 211,221 ****
--- 211,231 ----
        && (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
*** 1392,1403 ****
  		    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;
  
--- 1402,1412 ----
  		    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
*** 4516,4521 ****
--- 4525,4532 ----
  
    copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
  		     VAR_DECL, DECL_NAME (decl), type);
+   if (DECL_PT_UID_SET_P (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
*** 4541,4546 ****
--- 4552,4559 ----
  
    copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
  		     VAR_DECL, DECL_NAME (decl), type);
+   if (DECL_PT_UID_SET_P (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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree.c	2010-04-14 14:58:18.000000000 +0200
*************** 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_PT_UID_SET_P (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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree.h	2010-04-14 14:58:18.000000000 +0200
*************** struct function;
*** 2489,2494 ****
--- 2489,2503 ----
     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))
+ /* Whether the ..._DECL node pt-uid has been initialized and thus needs to
+    be preserved when copyin the decl.  */
+ #define DECL_PT_UID_SET_P(NODE) (DECL_MINIMAL_CHECK (NODE)->decl_minimal.pt_uid != -1u)
+ 
  /* 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 {
*** 2512,2517 ****
--- 2521,2527 ----
    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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-ssa-alias.h	2010-04-14 14:58:18.000000000 +0200
*************** 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/cfgexpand.c	2010-04-14 14:58:18.000000000 +0200
*************** update_alias_info_with_stack_vars (void)
*** 500,511 ****
        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;
--- 500,511 ----
        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)
*** 515,521 ****
  
        /* 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
--- 515,521 ----
  
        /* 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:58:18.000000000 +0200
***************
*** 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/gimple-pretty-print.c	2010-04-14 14:58:18.000000000 +0200
*************** dump_gimple_call_args (pretty_printer *b
*** 477,482 ****
--- 477,536 ----
      }
  }
  
+ /* 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, dump_flags, false);
+ 	      if (DECL_PT_UID (var) != DECL_UID (var))
+ 		{
+ 		  pp_string (buffer, "ptD.");
+ 		  pp_decimal_int (buffer, DECL_PT_UID (var));
+ 		}
+ 	    }
+ 	  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 ****
--- 540,564 ----
  {
    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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-dump.c	2010-04-14 14:58:18.000000000 +0200
*************** static const struct dump_option_value_in
*** 821,826 ****
--- 821,827 ----
    {"memsyms", TDF_MEMSYMS},
    {"verbose", TDF_VERBOSE},
    {"eh", TDF_EH},
+   {"alias", TDF_ALIAS},
    {"nouid", TDF_NOUID},
    {"all", ~(TDF_RAW | TDF_SLIM | TDF_LINENO | TDF_TREE | TDF_RTL | TDF_IPA
  	    | TDF_STMTADDR | TDF_GRAPH | TDF_DIAGNOSTIC | TDF_VERBOSE
Index: trunk/gcc/tree-pass.h
===================================================================
*** trunk.orig/gcc/tree-pass.h	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/tree-pass.h	2010-04-14 14:58:18.000000000 +0200
*************** enum tree_dump_index
*** 78,85 ****
  #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_NOUID	(1 << 20)	/* omit UIDs from dumps.  */
  
  /* In tree-dump.c */
  
--- 78,86 ----
  #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_NOUID	(1 << 20)	/* omit UIDs from dumps.  */
+ #define TDF_ALIAS	(1 << 21)	/* 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	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c	2010-04-14 14:58:18.000000000 +0200
*************** 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" } } */
Index: trunk/gcc/doc/invoke.texi
===================================================================
*** trunk.orig/gcc/doc/invoke.texi	2010-04-14 14:20:59.000000000 +0200
--- trunk/gcc/doc/invoke.texi	2010-04-14 14:58:18.000000000 +0200
*************** With this flag, the program debug info r
*** 6577,6584 ****
  
  @item -fipa-pta
  @opindex fipa-pta
! Perform interprocedural pointer analysis.  This option is experimental
! and does not affect generated code.
  
  @item -fipa-cp
  @opindex fipa-cp
--- 6577,6586 ----
  
  @item -fipa-pta
  @opindex fipa-pta
! Perform interprocedural pointer analysis and interprocedural modification
! and reference analysis.  This option can cause excessive memory and
! compile-time usage on large compilation units.  It is not enabled by
! default at any optimization level.
  
  @item -fipa-cp
  @opindex fipa-cp

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

end of thread, other threads:[~2011-01-24  5:21 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-14 14:55 [PATCH] Fix IPA-PTA, implement IPA mod/ref analysis Richard Guenther
2010-04-15 22:22 ` Steven Bosscher
2010-04-16  8:57   ` Richard Guenther
2010-04-16 13:45     ` Richard Guenther
2011-01-24 11:42 ` H.J. Lu

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