diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index d6bbad99cc8001ae85e7ee8aded30650d78f1a5c..b1865d5be9a071a541efa4804dc277e2d3bff543 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,68 @@
+2010-04-15  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.
+	* tree-pretty-print.c (dump_decl_name): Dump DECL_PT_UID if asked to.
+	* doc/invoke.texi (-fipa-pta): Update documentation.
+
 2010-04-15  Richard Guenther  <rguenther@suse.de>
 
 	* Makefile.in (OBJS-common): Add gimple-fold.o.
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 454f61a4175178ed80dbe6ef032fb88d8948bf20..48173d9b992b4e795cc83c1157d41a272f27f7e9 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -500,12 +500,12 @@ update_alias_info_with_stack_vars (void)
       for (j = i; j != EOC; j = stack_vars[j].next)
 	{
 	  tree decl = stack_vars[j].decl;
-	  unsigned int uid = DECL_UID (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 (uid));
+		      && referenced_var_lookup (DECL_UID (decl)));
 	  bitmap_set_bit (part, uid);
 	  *((bitmap *) pointer_map_insert (decls_to_partitions,
 					   (void *)(size_t) uid)) = part;
@@ -515,7 +515,7 @@ update_alias_info_with_stack_vars (void)
 
       /* Make the SSA name point to all partition members.  */
       pi = get_ptr_info (name);
-      pt_solution_set (&pi->pt, part);
+      pt_solution_set (&pi->pt, part, false, false);
     }
 
   /* Make all points-to sets that contain one member of a partition
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1c52a3a54648e0eb61ec2172cab0f1f409a85413..ad8eff8a983fb8af005dafc84bbb74d33c74e797 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -6577,8 +6577,10 @@ With this flag, the program debug info reflects a new structure layout.
 
 @item -fipa-pta
 @opindex fipa-pta
-Perform interprocedural pointer analysis.  This option is experimental
-and does not affect generated code.
+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
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 6329d51f2da65b3a58faf65ea8ef01b35b735bd7..0f386163ab3ebdc56010c3bd219f8224e3e511f5 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -477,6 +477,60 @@ dump_gimple_call_args (pretty_printer *buffer, gimple gs, int flags)
     }
 }
 
+/* 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.  */
@@ -486,6 +540,25 @@ dump_gimple_call (pretty_printer *buffer, gimple gs, int spc, int flags)
 {
   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",
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 25024a3ee419ca6527ba51e29cc0cdbee514217b..c022447779f719237127aec6b73d75204d9781e4 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,21 @@
+2010-04-15  Richard Guenther  <rguenther@suse.de>
+
+	* 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.
+
 2010-04-14  Bernd Schmidt  <bernd.schmidt@codesourcery.com>
 
 	PR target/21803
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..a56e71a4adebc00354c1a20ce81a0edf7eae2b9b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c
new file mode 100644
index 0000000000000000000000000000000000000000..2dc6eae25d25c60eb15d4031324c435b46c9a032
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-11.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-11.c
new file mode 100644
index 0000000000000000000000000000000000000000..947ab816fc9e6e9d78599b59fc05a9e868b29c19
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-11.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-12.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-12.c
new file mode 100644
index 0000000000000000000000000000000000000000..1c773eed027dd6aac73577d0268ddff06a9bcce6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-12.c
@@ -0,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;
+}
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
new file mode 100644
index 0000000000000000000000000000000000000000..8c2c8b6183f2a59f635ac5ebf9d0eb57f4b9eb44
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..a6c7e4b4786c349218496399702e9c24ab95bcda
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c
new file mode 100644
index 0000000000000000000000000000000000000000..e73db1c95ac37c6c93c4c23bf3e533fc0f46d68e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2b901cf910967b073d82742a21416a77f937ead
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c
new file mode 100644
index 0000000000000000000000000000000000000000..3359c534aaba54e5c87db61db71c1e7fcc297bd1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c
new file mode 100644
index 0000000000000000000000000000000000000000..aaa6090ddd7225b61e1bbbe3c3fa774285dcc6f2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c
@@ -0,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" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c
new file mode 100644
index 0000000000000000000000000000000000000000..3cdfd63fa3e305e25185171f8ec153f97a5e7c99
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c
@@ -0,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;
+}
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c
new file mode 100644
index 0000000000000000000000000000000000000000..5bedc9d8a6550df8f772982c012a0ba5351f120b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c
@@ -0,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;
+}
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c
new file mode 100644
index 0000000000000000000000000000000000000000..1a98da3978ec145d8b83b9df9ac73f5e20b8d3f2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c
@@ -0,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;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c b/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
index 7df130a1f5b9dcd325747d4a53cbbda708a71b25..6d0d032613f66f6cb48d5803609c415599d3d944 100644
--- a/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
+++ b/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
@@ -42,5 +42,5 @@ void test4 (int a4, char b, char c, char d, char e, char f, char g, char h)
   bar (p);
 }
 
-/* { dg-final { scan-ipa-dump "bar.arg0 = { a4 a3 a2 a1 }" "pta" } } */
+/* { dg-final { scan-ipa-dump "bar.arg0 = { test4.arg0 test3.arg0 test2.arg0 test1.arg0 }" "pta" } } */
 /* { dg-final { cleanup-ipa-dump "pta" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ipa-pta-2.c b/gcc/testsuite/gcc.dg/torture/ipa-pta-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..768c99e823ea73d71b82c68029c3a08b11136b09
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ipa-pta-2.c
@@ -0,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;
+}
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index bd91e3b60a872362fd578151e96fd4caabc0f923..a8415b5a825ef279cd32debab7b31d7864fa8459 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -269,6 +269,8 @@ dump_variable (FILE *file, tree var)
   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);
diff --git a/gcc/tree-dump.c b/gcc/tree-dump.c
index 20b7c716f80ac4fc870055f550178a25bf53fb89..5467e28b5e8693f964f144c336e786f10c840c2c 100644
--- a/gcc/tree-dump.c
+++ b/gcc/tree-dump.c
@@ -821,6 +821,7 @@ static const struct dump_option_value_info dump_options[] =
   {"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
diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h
index 2f9cf3df10acf814a869cb0784678f560e943964..fda7a86f2d03905bb97b5da38af920b4ecacde94 100644
--- a/gcc/tree-flow.h
+++ b/gcc/tree-flow.h
@@ -76,6 +76,9 @@ struct GTY(()) gimple_df {
   /* 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;
 };
 
@@ -111,9 +114,10 @@ typedef struct
 ---------------------------------------------------------------------------*/
 
 /* Aliasing information for SSA_NAMEs representing pointer variables.  */
+
 struct GTY(()) ptr_info_def
 {
-  /* The points-to solution, TBAA-pruned if the pointer is dereferenced.  */
+  /* The points-to solution.  */
   struct pt_solution pt;
 };
 
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 922ce52dd9dbd1ed0444a6912ac2d189aca9d91b..b564fa58a74acd76376a5d59bbac167961a3a9ac 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -211,11 +211,21 @@ remap_ssa_name (tree name, copy_body_data *id)
       && (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
@@ -1392,12 +1402,11 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id)
 		    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);
+	      /* 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;
 
@@ -4516,6 +4525,8 @@ copy_decl_to_var (tree decl, copy_body_data *id)
 
   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);
@@ -4541,6 +4552,8 @@ copy_result_decl_to_var (tree decl, copy_body_data *id)
 
   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))
diff --git a/gcc/tree-into-ssa.c b/gcc/tree-into-ssa.c
index c90049ed337016e963b8192b64db9e19c1739622..ead1244936cdcad167fd820fb95b2d31e01da302 100644
--- a/gcc/tree-into-ssa.c
+++ b/gcc/tree-into-ssa.c
@@ -1475,7 +1475,15 @@ dump_decl_set (FILE *file, bitmap set)
 
       EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
 	{
-	  print_generic_expr (file, referenced_var (i), 0);
+	  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, " ");
 	}
 
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 94726722d259a60bae516ae0a827f3da689953be..019b9332b81bae073fa9a5baafccc937f4eb8545 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -78,8 +78,9 @@ enum tree_dump_index
 #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 */
 
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index 6a40b6f42d1a912250546066f09f082c13d1e094..331d93d6178a289a97261728df60ec9c3cc06dbe 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -199,6 +199,13 @@ dump_decl_name (pretty_printer *buffer, tree node, int flags)
 	    pp_printf (buffer, "%c.%u", c, DECL_UID (node));
 	}
     }
+  if ((flags & TDF_ALIAS) && DECL_PT_UID (node) != DECL_UID (node))
+    {
+      if (flags & TDF_NOUID)
+	pp_printf (buffer, "ptD.xxxx");
+      else
+	pp_printf (buffer, "ptD.%u", DECL_PT_UID (node));
+    }
 }
 
 /* Like the above, but used for pretty printing function calls.  */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 7f09df8d16c3a58a3825eb0a9dfa9c0e87b70ead..8b1fdc37556574baa41ce47f24869360f39b55a9 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -214,7 +214,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
   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 bitmap_bit_p (pi->pt.vars, DECL_PT_UID (decl));
 
   return pt_solution_includes (&pi->pt, decl);
 }
@@ -401,6 +401,9 @@ dump_points_to_solution (FILE *file, struct pt_solution *pt)
   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");
 
@@ -410,6 +413,8 @@ dump_points_to_solution (FILE *file, struct pt_solution *pt)
       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)");
     }
 }
 
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 289fb86d04ca45d60d6d784a7082d255827da0bb..147f368843488de9f6c71280f1c440b5fbaaeb25 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -38,9 +38,14 @@ struct GTY(()) pt_solution
      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.  */
+  /* 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;
@@ -118,14 +123,17 @@ extern void dump_alias_stats (FILE *);
 /* 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);
+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  */
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index 85493a2dd503e4ba5db8037200eb8112b9bedf81..ad4c10d326127bd918009919de3c48139bc23053 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -161,6 +161,48 @@
   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;
 
@@ -236,6 +278,9 @@ struct variable_info
   /* 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;
 
@@ -368,6 +413,7 @@ new_var_info (tree t, const char *name)
   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);
@@ -677,11 +723,11 @@ debug_constraint (constraint_t c)
 /* Print out all constraints to FILE */
 
 static void
-dump_constraints (FILE *file)
+dump_constraints (FILE *file, int from)
 {
   int i;
   constraint_t c;
-  for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
+  for (i = from; VEC_iterate (constraint_t, constraints, i, c); i++)
     dump_constraint (file, c);
 }
 
@@ -690,7 +736,7 @@ dump_constraints (FILE *file)
 void
 debug_constraints (void)
 {
-  dump_constraints (stderr);
+  dump_constraints (stderr, 0);
 }
 
 /* Print out to FILE the edge in the constraint graph that is created by
@@ -741,7 +787,7 @@ dump_constraint_graph (FILE *file)
   /* 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);
+  dump_constraints (file, 0);
   fprintf (file, "*/\n");
 
   /* Prints the header of the dot file:  */
@@ -3506,7 +3552,7 @@ make_constraint_from_heapvar (varinfo_t lhs, const char *name)
 
   /* 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))
+  if (cfun && gimple_referenced_vars (cfun))
     add_referenced_var (heapvar);
 
   vi = new_var_info (heapvar, name);
@@ -3539,6 +3585,49 @@ make_constraint_from_restrict (varinfo_t lhs, const char *name)
   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)
+    {
+      varinfo_t ai = first_vi_for_offset (fi, part);
+      c.var = ai ? ai->id : anything_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.  */
 
@@ -3728,6 +3817,40 @@ handle_pure_call (gimple stmt, VEC(ce_s, heap) **results)
   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
@@ -3740,6 +3863,7 @@ find_func_aliases (gimple origt)
   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)
@@ -3892,6 +4016,88 @@ find_func_aliases (gimple origt)
 	  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...  */
@@ -3900,7 +4106,8 @@ find_func_aliases (gimple origt)
 	  }
       if (!in_ipa_mode
 	  || (fndecl
-	      && !lookup_vi_for_tree (fndecl)))
+	      && (!(fi = lookup_vi_for_tree (fndecl))
+		  || !fi->is_fn_info)))
 	{
 	  VEC(ce_s, heap) *rhsc = NULL;
 	  int flags = gimple_call_flags (t);
@@ -3928,24 +4135,9 @@ find_func_aliases (gimple origt)
       else
 	{
 	  tree lhsop;
-	  varinfo_t fi;
-	  int i = 1;
-	  size_t j;
-	  tree decl;
+	  unsigned j;
 
-	  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);
-	    }
+	  fi = get_fi_for_callee (t);
 
 	  /* Assign all the passed arguments to the appropriate incoming
 	     parameters of the function.  */
@@ -3955,51 +4147,70 @@ find_func_aliases (gimple origt)
 	      struct constraint_expr *rhsp;
 	      tree arg = gimple_call_arg (t, j);
 
+	      if (!could_have_pointers (arg))
+		continue;
+
 	      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;
-		}
+	      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);
 		}
-	      i++;
 	    }
 
 	  /* If we are returning a value, assign it to the result.  */
-	  if (lhsop)
+	  lhsop = gimple_call_lhs (t);
+	  if (lhsop
+	      && could_have_pointers (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 = get_function_part_constraint (fi, fi_result);
+	      if (fndecl
+		  && DECL_RESULT (fndecl)
+		  && DECL_BY_REFERENCE (DECL_RESULT (fndecl)))
 		{
-		  rhs.type = SCALAR;
-		  rhs.var = first_vi_for_offset (fi, i)->id;
-		  rhs.offset = 0;
+		  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
@@ -4039,7 +4250,9 @@ find_func_aliases (gimple origt)
       /* 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))
+	  && 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.  */
@@ -4063,7 +4276,22 @@ find_func_aliases (gimple origt)
 	   && gimple_return_retval (t) != NULL_TREE
 	   && could_have_pointers (gimple_return_retval (t)))
     {
-      make_escape_constraint (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)
@@ -4132,6 +4360,283 @@ find_func_aliases (gimple origt)
 }
 
 
+/* 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.  */
 
@@ -4204,34 +4709,6 @@ insert_into_field_list (varinfo_t base, varinfo_t field)
   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
@@ -4432,35 +4909,125 @@ count_num_arguments (tree decl, bool *is_varargs)
 static unsigned int
 create_function_info_for (tree decl, const char *name)
 {
-  varinfo_t vi;
+  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 = count_num_arguments (decl, &is_varargs) + 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++;
 
-  /* If it's varargs, we don't know how many arguments it has, so we
-     can't do much.  */
-  if (is_varargs)
+  prev_vi = vi;
+
+  /* Create a variable for things the function clobbers and one for
+     things the function uses.  */
     {
-      vi->fullsize = ~0;
-      vi->size = ~0;
-      vi->is_unknown_size_var = true;
-      return vi->id;
+      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++;
     }
 
-  arg = DECL_ARGUMENTS (decl);
+  /* 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.  */
-  for (i = 1; i < vi->fullsize; i++)
+  arg = DECL_ARGUMENTS (decl);
+  for (i = 0; i < num_args; i++)
     {
       varinfo_t argvi;
       const char *newname;
@@ -4470,17 +5037,21 @@ create_function_info_for (tree decl, const char *name)
       if (arg)
 	argdecl = arg;
 
-      asprintf (&tempname, "%s.arg%d", name, i-1);
+      asprintf (&tempname, "%s.arg%d", name, i);
       newname = ggc_strdup (tempname);
       free (tempname);
 
       argvi = new_var_info (argdecl, newname);
-      argvi->offset = i;
+      argvi->offset = fi_parm_base + 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)
+	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);
@@ -4488,33 +5059,32 @@ create_function_info_for (tree decl, const char *name)
 	}
     }
 
-  /* Create a variable for the return var.  */
-  if (DECL_RESULT (decl) != NULL
-      || !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl))))
+  /* Add one representative for all further args.  */
+  if (is_varargs)
     {
-      varinfo_t resultvi;
+      varinfo_t argvi;
       const char *newname;
       char *tempname;
-      tree resultdecl = decl;
-
-      vi->fullsize ++;
-
-      if (DECL_RESULT (decl))
-	resultdecl = DECL_RESULT (decl);
+      tree decl;
 
-      asprintf (&tempname, "%s.result", name);
+      asprintf (&tempname, "%s.varargs", 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);
+      /* 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;
@@ -4575,14 +5145,54 @@ create_variable_info_for (tree decl, const char *name)
     }
 
   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
-      && (!flag_whole_program || !in_ipa_mode)
       && 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");
-      make_copy_constraint (vi, nonlocal_id);
+
+      /* 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++;
@@ -4638,7 +5248,6 @@ create_variable_info_for (tree decl, const char *name)
       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)
@@ -4695,20 +5304,19 @@ dump_solution_for_var (FILE *file, unsigned int var)
   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");
-    }
+  /* 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.  */
@@ -4888,9 +5496,15 @@ set_uids_in_ptset (bitmap into, bitmap from, struct pt_solution *pt)
 	  || 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_UID (vi->decl));
+	  bitmap_set_bit (into, DECL_PT_UID (vi->decl));
 	  if (vi->is_global_var)
 	    pt->vars_contains_global = true;
 	}
@@ -4926,7 +5540,12 @@ find_what_var_points_to (varinfo_t orig_vi, struct pt_solution *pt)
 	  if (vi->id == nothing_id)
 	    pt->null = 1;
 	  else if (vi->id == escaped_id)
-	    pt->escaped = 1;
+	    {
+	      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)
@@ -5032,30 +5651,53 @@ pt_solution_reset (struct pt_solution *pt)
 }
 
 /* Set the points-to solution *PT to point only to the variables
-   in VARS.  */
+   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)
+pt_solution_set (struct pt_solution *pt, bitmap vars,
+		 bool vars_contains_global, bool vars_contains_restrict)
 {
-  bitmap_iterator bi;
-  unsigned i;
-
   memset (pt, 0, sizeof (struct pt_solution));
   pt->vars = vars;
-  EXECUTE_IF_SET_IN_BITMAP (vars, 0, i, bi)
+  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)
     {
-      tree var = referenced_var_lookup (i);
-      if (is_global_var (var))
-	{
-	  pt->vars_contains_global = true;
-	  break;
-	}
+      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.  */
 
-static bool
+bool
 pt_solution_empty_p (struct pt_solution *pt)
 {
   if (pt->anything
@@ -5071,6 +5713,11 @@ pt_solution_empty_p (struct pt_solution *pt)
       && !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;
 }
 
@@ -5087,6 +5734,15 @@ pt_solution_includes_global (struct pt_solution *pt)
   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;
 }
 
@@ -5104,7 +5760,7 @@ pt_solution_includes_1 (struct pt_solution *pt, const_tree decl)
     return true;
 
   if (pt->vars
-      && bitmap_bit_p (pt->vars, DECL_UID (decl)))
+      && bitmap_bit_p (pt->vars, DECL_PT_UID (decl)))
     return true;
 
   /* If the solution includes ESCAPED, check it.  */
@@ -5112,6 +5768,11 @@ pt_solution_includes_1 (struct pt_solution *pt, const_tree decl)
       && 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;
 }
 
@@ -5162,6 +5823,25 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
 	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
@@ -5227,7 +5907,18 @@ dump_sa_points_to_info (FILE *outfile)
     }
 
   for (i = 0; i < VEC_length (varinfo_t, varmap); i++)
-    dump_solution_for_var (outfile, 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);
+    }
 }
 
 
@@ -5499,12 +6190,6 @@ solve_constraints (void)
 {
   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 "
@@ -5573,7 +6258,7 @@ compute_points_to_sets (void)
 
   intra_create_variable_infos ();
 
-  /* Now walk all statements and derive aliases.  */
+  /* Now walk all statements and build the constraint set.  */
   FOR_EACH_BB (bb)
     {
       gimple_stmt_iterator gsi;
@@ -5594,6 +6279,12 @@ compute_points_to_sets (void)
 	}
     }
 
+  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 ();
 
@@ -5724,6 +6415,23 @@ delete_points_to_sets (void)
 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.  */
@@ -5808,11 +6516,17 @@ gate_ipa_pta (void)
 	  && !(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;
 
@@ -5829,16 +6543,23 @@ ipa_pta_execute (void)
 	  || 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));
     }
 
+  /* 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;
@@ -5885,16 +6606,204 @@ ipa_pta_execute (void)
 	      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;
diff --git a/gcc/tree.c b/gcc/tree.c
index 83f1237fd85d84c00005ec9ee77b7a9241f48d40..6ac1318667517c8ebde35df5bb4f6e27bee5e360 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -883,7 +883,10 @@ make_node_stat (enum tree_code code MEM_STAT_DECL)
       if (TREE_CODE (t) == DEBUG_EXPR_DECL)
 	DECL_UID (t) = --next_debug_decl_uid;
       else
-	DECL_UID (t) = next_decl_uid++;
+	{
+	  DECL_UID (t) = next_decl_uid++;
+	  SET_DECL_PT_UID (t, -1);
+	}
       if (TREE_CODE (t) == LABEL_DECL)
 	LABEL_DECL_UID (t) = -1;
 
@@ -963,7 +966,11 @@ copy_node_stat (tree node MEM_STAT_DECL)
       if (code == DEBUG_EXPR_DECL)
 	DECL_UID (t) = --next_debug_decl_uid;
       else
-	DECL_UID (t) = next_decl_uid++;
+	{
+	  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))
 	{
diff --git a/gcc/tree.h b/gcc/tree.h
index 8c979c066be3fc95f679b0e5f940b999d2daa793..ad958119a222689ea5c4964925982b5532f154a3 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2489,6 +2489,15 @@ struct function;
    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
@@ -2512,6 +2521,7 @@ struct GTY(()) tree_decl_minimal {
   struct tree_common common;
   location_t locus;
   unsigned int uid;
+  unsigned int pt_uid;
   tree name;
   tree context;
 };