summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorrguenth <rguenth@138bc75d-0d04-0410-961f-82ee72b054a4>2010-04-15 13:16:44 +0000
committerrguenth <rguenth@138bc75d-0d04-0410-961f-82ee72b054a4>2010-04-15 13:16:44 +0000
commit1a981e1a975098bd537556d2b36f275f3bc075c7 (patch)
tree381556e2947cff452ab7c67fc87d183b8ec9da09 /gcc
parent2d18b16deada5c2be61ffbaef3a39df85849f680 (diff)
downloadgcc-1a981e1a975098bd537556d2b36f275f3bc075c7.tar.gz
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. * 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@158374 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog65
-rw-r--r--gcc/cfgexpand.c6
-rw-r--r--gcc/doc/invoke.texi6
-rw-r--r--gcc/gimple-pretty-print.c73
-rw-r--r--gcc/testsuite/ChangeLog18
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c50
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c30
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-11.c33
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-12.c34
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c56
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c25
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c28
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c33
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c26
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c25
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c30
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c31
-rw-r--r--gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c17
-rw-r--r--gcc/testsuite/gcc.dg/torture/ipa-pta-1.c2
-rw-r--r--gcc/testsuite/gcc.dg/torture/ipa-pta-2.c24
-rw-r--r--gcc/tree-dfa.c2
-rw-r--r--gcc/tree-dump.c1
-rw-r--r--gcc/tree-flow.h6
-rw-r--r--gcc/tree-inline.c25
-rw-r--r--gcc/tree-into-ssa.c10
-rw-r--r--gcc/tree-pass.h3
-rw-r--r--gcc/tree-pretty-print.c7
-rw-r--r--gcc/tree-ssa-alias.c7
-rw-r--r--gcc/tree-ssa-alias.h12
-rw-r--r--gcc/tree-ssa-structalias.c1229
-rw-r--r--gcc/tree.c11
-rw-r--r--gcc/tree.h10
32 files changed, 1755 insertions, 180 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index d6bbad99cc8..b1865d5be9a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,70 @@
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.
(gimple-fold.o): New rule.
* tree.h (maybe_fold_offset_to_reference,
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 454f61a4175..48173d9b992 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 1c52a3a5464..ad8eff8a983 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 6329d51f2da..0f386163ab3 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 25024a3ee41..c022447779f 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 00000000000..a56e71a4ade
--- /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 00000000000..2dc6eae25d2
--- /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 00000000000..947ab816fc9
--- /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 00000000000..1c773eed027
--- /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 00000000000..8c2c8b6183f
--- /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 00000000000..a6c7e4b4786
--- /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 00000000000..e73db1c95ac
--- /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 00000000000..d2b901cf910
--- /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 00000000000..3359c534aab
--- /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 00000000000..aaa6090ddd7
--- /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 00000000000..3cdfd63fa3e
--- /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 00000000000..5bedc9d8a65
--- /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 00000000000..1a98da3978e
--- /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 7df130a1f5b..6d0d032613f 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 00000000000..768c99e823e
--- /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 bd91e3b60a8..a8415b5a825 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 20b7c716f80..5467e28b5e8 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 2f9cf3df10a..fda7a86f2d0 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 922ce52dd9d..b564fa58a74 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 c90049ed337..ead1244936c 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 94726722d25..019b9332b81 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 6a40b6f42d1..331d93d6178 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 7f09df8d16c..8b1fdc37556 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 289fb86d04c..147f3688434 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 85493a2dd50..ad4c10d3261 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);
+ }
}
@@ -5500,12 +6191,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 "
"substitution\n");
@@ -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 83f1237fd85..6ac13186675 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 8c979c066be..ad958119a22 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;
};