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