From 25a6a8731ca102e1995fe996722a0637b1311445 Mon Sep 17 00:00:00 2001 From: Richard Guenther Date: Thu, 15 Apr 2010 13:16:44 +0000 Subject: [PATCH] tree-ssa-structalias.c (struct variable_info): Add is_fn_info flag. 2010-04-15 Richard Guenther * 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. From-SVN: r158374 --- gcc/ChangeLog | 65 ++ gcc/cfgexpand.c | 6 +- gcc/doc/invoke.texi | 6 +- gcc/gimple-pretty-print.c | 73 ++ gcc/testsuite/ChangeLog | 18 + gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c | 50 + gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c | 30 + gcc/testsuite/gcc.dg/ipa/ipa-pta-11.c | 33 + gcc/testsuite/gcc.dg/ipa/ipa-pta-12.c | 34 + gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c | 56 + gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c | 25 + gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c | 28 + gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c | 33 + gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c | 26 + gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c | 25 + gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c | 30 + gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c | 31 + gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c | 17 + gcc/testsuite/gcc.dg/torture/ipa-pta-1.c | 2 +- gcc/testsuite/gcc.dg/torture/ipa-pta-2.c | 24 + gcc/tree-dfa.c | 2 + gcc/tree-dump.c | 1 + gcc/tree-flow.h | 6 +- gcc/tree-inline.c | 25 +- gcc/tree-into-ssa.c | 10 +- gcc/tree-pass.h | 3 +- gcc/tree-pretty-print.c | 7 + gcc/tree-ssa-alias.c | 7 +- gcc/tree-ssa-alias.h | 12 +- gcc/tree-ssa-structalias.c | 1229 +++++++++++++++++++--- gcc/tree.c | 11 +- gcc/tree.h | 10 + 32 files changed, 1755 insertions(+), 180 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-11.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-12.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c create mode 100644 gcc/testsuite/gcc.dg/torture/ipa-pta-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d6bbad99cc8..b1865d5be9a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,68 @@ +2010-04-15 Richard Guenther + + * 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 * Makefile.in (OBJS-common): Add gimple-fold.o. 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 + + * 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 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 + +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); + } } @@ -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 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; }; -- 2.30.2