[PATCH] Fix PR39120, missing escape constraints for struct copies in calls
Richard Guenther
rguenther@suse.de
Fri Mar 27 23:00:00 GMT 2009
This merges the fix for PR39120 from alias-improvements. It is also
a candidate for 4.4.1, so to ease merging it back this is split out
from the alias-improvements branch merge.
Bootstrapped and tested on x86_64-unknown-linux-gnu, applied to trunk.
Richard.
2009-03-24 Richard Guenther <rguenther@suse.de>
PR tree-optimization/39120
* tree-ssa-structalias.c (do_sd_constraint): Do not use CALLUSED
as a representative.
(solve_graph): Do propagate CALLUSED.
(handle_pure_call): Use a scalar constraint from CALLUSED for
the return value.
(find_what_p_points_to): CALLUSED shall not appear in poins-to
solutions.
* gcc.dg/torture/pta-callused-1.c: New testcase.
Index: gcc/tree-ssa-structalias.c
===================================================================
*** gcc/tree-ssa-structalias.c.orig 2009-03-24 15:18:22.000000000 +0100
--- gcc/tree-ssa-structalias.c 2009-03-24 15:33:32.000000000 +0100
*************** do_sd_constraint (constraint_graph_t gra
*** 1592,1603 ****
if (get_varinfo (t)->is_special_var)
flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
/* Merging the solution from ESCAPED needlessly increases
! the set. Use ESCAPED as representative instead.
! Same for CALLUSED. */
else if (get_varinfo (t)->id == find (escaped_id))
flag |= bitmap_set_bit (sol, escaped_id);
- else if (get_varinfo (t)->id == find (callused_id))
- flag |= bitmap_set_bit (sol, callused_id);
else if (add_graph_edge (graph, lhs, t))
flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
}
--- 1592,1600 ----
if (get_varinfo (t)->is_special_var)
flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
/* Merging the solution from ESCAPED needlessly increases
! the set. Use ESCAPED as representative instead. */
else if (get_varinfo (t)->id == find (escaped_id))
flag |= bitmap_set_bit (sol, escaped_id);
else if (add_graph_edge (graph, lhs, t))
flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
}
*************** solve_graph (constraint_graph_t graph)
*** 2516,2524 ****
solution_empty = bitmap_empty_p (solution);
if (!solution_empty
! /* Do not propagate the ESCAPED/CALLUSED solutions. */
! && i != find (escaped_id)
! && i != find (callused_id))
{
bitmap_iterator bi;
--- 2513,2520 ----
solution_empty = bitmap_empty_p (solution);
if (!solution_empty
! /* Do not propagate the ESCAPED solutions. */
! && i != find (escaped_id))
{
bitmap_iterator bi;
*************** handle_pure_call (gimple stmt)
*** 3674,3680 ****
and globals will be dealt with in handle_lhs_call. */
rhsc.var = callused_id;
rhsc.offset = 0;
! rhsc.type = ADDRESSOF;
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
process_constraint (new_constraint (*lhsp, rhsc));
VEC_free (ce_s, heap, lhsc);
--- 3670,3676 ----
and globals will be dealt with in handle_lhs_call. */
rhsc.var = callused_id;
rhsc.offset = 0;
! rhsc.type = SCALAR;
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
process_constraint (new_constraint (*lhsp, rhsc));
VEC_free (ce_s, heap, lhsc);
*************** find_what_p_points_to (tree p)
*** 4925,4933 ****
pi->pt_null = 1;
else if (vi->id == anything_id
|| vi->id == nonlocal_id
! || vi->id == escaped_id
! || vi->id == callused_id)
was_pt_anything = 1;
else if (vi->id == readonly_id)
was_pt_anything = 1;
else if (vi->id == integer_id)
--- 4921,4930 ----
pi->pt_null = 1;
else if (vi->id == anything_id
|| vi->id == nonlocal_id
! || vi->id == escaped_id)
was_pt_anything = 1;
+ else if (vi->id == callused_id)
+ gcc_unreachable ();
else if (vi->id == readonly_id)
was_pt_anything = 1;
else if (vi->id == integer_id)
Index: gcc/testsuite/gcc.dg/torture/pta-callused-1.c
===================================================================
*** /dev/null 1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/gcc.dg/torture/pta-callused-1.c 2009-03-24 15:33:56.000000000 +0100
***************
*** 0 ****
--- 1,25 ----
+ /* { dg-do run } */
+ /* { dg-options "-fdump-tree-alias" } */
+ /* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
+
+ volatile int i;
+ int ** __attribute__((noinline,pure)) foo(int **p) { i; return p; }
+ int bar(void)
+ {
+ int i = 0, j = 1;
+ int *p, **q;
+ p = &i;
+ q = foo(&p);
+ *q = &j;
+ return *p;
+ }
+ extern void abort (void);
+ int main()
+ {
+ if (bar() != 1)
+ abort ();
+ return 0;
+ }
+
+ /* { dg-final { scan-tree-dump "p.._., name memory tag: NMT..., is dereferenced, points-to vars: { i j }" "alias" } } */
+ /* { dg-final { cleanup-tree-dump "alias" } } */
2009-03-24 Richard Guenther <rguenther@suse.de>
PR tree-optimization/39120
* tree-ssa-structalias.c (handle_rhs_call): Fill out return
constraints.
(handle_lhs_call): Process return constraints. Add escape
constraints if necessary.
(handle_const_call): Fill out return constraints. Make nested
case more precise. Avoid consttmp if possible.
(handle_pure_call): Fill out return constraints. Avoid
callused if possible.
(find_func_aliases): Simplify call handling.
* gcc.c-torture/execute/pr39120.c: New testcase.
Index: trunk/gcc/testsuite/gcc.c-torture/execute/pr39120.c
===================================================================
*** /dev/null 1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.c-torture/execute/pr39120.c 2009-03-24 15:19:16.000000000 +0100
***************
*** 0 ****
--- 1,18 ----
+ struct X { int *p; } x;
+
+ struct X __attribute__((noinline))
+ foo(int *p) { struct X x; x.p = p; return x; }
+
+ void __attribute((noinline))
+ bar() { *x.p = 1; }
+
+ extern void abort (void);
+ int main()
+ {
+ int i = 0;
+ x = foo(&i);
+ bar();
+ if (i != 1)
+ abort ();
+ return 0;
+ }
Index: trunk/gcc/tree-ssa-structalias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-structalias.c 2009-03-24 15:18:38.000000000 +0100
--- trunk/gcc/tree-ssa-structalias.c 2009-03-24 15:30:22.000000000 +0100
*************** make_escape_constraint (tree op)
*** 3484,3491 ****
RHS. */
static void
! handle_rhs_call (gimple stmt)
{
unsigned i;
for (i = 0; i < gimple_call_num_args (stmt); ++i)
--- 3484,3492 ----
RHS. */
static void
! handle_rhs_call (gimple stmt, VEC(ce_s, heap) **results)
{
+ struct constraint_expr rhsc;
unsigned i;
for (i = 0; i < gimple_call_num_args (stmt); ++i)
*************** handle_rhs_call (gimple stmt)
*** 3501,3506 ****
--- 3502,3513 ----
/* The static chain escapes as well. */
if (gimple_call_chain (stmt))
make_escape_constraint (gimple_call_chain (stmt));
+
+ /* Regular functions return escaped addresses. */
+ rhsc.var = escaped_id;
+ rhsc.offset = 0;
+ rhsc.type = ADDRESSOF;
+ VEC_safe_push (ce_s, heap, *results, &rhsc);
}
/* For non-IPA mode, generate constraints necessary for a call
*************** handle_rhs_call (gimple stmt)
*** 3508,3517 ****
the LHS point to global and escaped variables. */
static void
! handle_lhs_call (tree lhs, int flags)
{
VEC(ce_s, heap) *lhsc = NULL;
- struct constraint_expr rhsc;
unsigned int j;
struct constraint_expr *lhsp;
--- 3515,3523 ----
the LHS point to global and escaped variables. */
static void
! handle_lhs_call (tree lhs, int flags, VEC(ce_s, heap) *rhsc)
{
VEC(ce_s, heap) *lhsc = NULL;
unsigned int j;
struct constraint_expr *lhsp;
*************** handle_lhs_call (tree lhs, int flags)
*** 3519,3524 ****
--- 3525,3531 ----
if (flags & ECF_MALLOC)
{
+ struct constraint_expr rhsc;
tree heapvar = heapvar_lookup (lhs);
varinfo_t vi;
*************** handle_lhs_call (tree lhs, int flags)
*** 3542,3556 ****
vi->size = ~0;
rhsc.type = ADDRESSOF;
rhsc.offset = 0;
}
! else
{
! rhsc.var = escaped_id;
! rhsc.offset = 0;
! rhsc.type = ADDRESSOF;
}
- for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
- process_constraint (new_constraint (*lhsp, rhsc));
VEC_free (ce_s, heap, lhsc);
}
--- 3549,3578 ----
vi->size = ~0;
rhsc.type = ADDRESSOF;
rhsc.offset = 0;
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ process_constraint (new_constraint (*lhsp, rhsc));
}
! else if (VEC_length (ce_s, rhsc) > 0)
{
! struct constraint_expr *lhsp, *rhsp;
! unsigned int i, j;
! /* If the store is to a global decl make sure to
! add proper escape constraints. */
! lhs = get_base_address (lhs);
! if (lhs
! && DECL_P (lhs)
! && is_global_var (lhs))
! {
! struct constraint_expr tmpc;
! tmpc.var = escaped_id;
! tmpc.offset = 0;
! tmpc.type = SCALAR;
! VEC_safe_push (ce_s, heap, lhsc, &tmpc);
! }
! for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); ++i)
! for (j = 0; VEC_iterate (ce_s, rhsc, j, rhsp); ++j)
! process_constraint (new_constraint (*lhsp, *rhsp));
}
VEC_free (ce_s, heap, lhsc);
}
*************** handle_lhs_call (tree lhs, int flags)
*** 3558,3600 ****
const function that returns a pointer in the statement STMT. */
static void
! handle_const_call (gimple stmt)
{
! tree lhs = gimple_call_lhs (stmt);
! VEC(ce_s, heap) *lhsc = NULL;
! struct constraint_expr rhsc;
! unsigned int j, k;
! struct constraint_expr *lhsp;
! tree tmpvar;
! struct constraint_expr tmpc;
! get_constraint_for (lhs, &lhsc);
!
! /* If this is a nested function then it can return anything. */
if (gimple_call_chain (stmt))
{
! rhsc.var = anything_id;
rhsc.offset = 0;
! rhsc.type = ADDRESSOF;
! for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
! process_constraint (new_constraint (*lhsp, rhsc));
! VEC_free (ce_s, heap, lhsc);
! return;
}
- /* We always use a temporary here, otherwise we end up with a quadratic
- amount of constraints for
- large_struct = const_call (large_struct);
- in field-sensitive PTA. */
- tmpvar = create_tmp_var_raw (ptr_type_node, "consttmp");
- tmpc = get_constraint_exp_for_temp (tmpvar);
-
- /* May return addresses of globals. */
- rhsc.var = nonlocal_id;
- rhsc.offset = 0;
- rhsc.type = ADDRESSOF;
- process_constraint (new_constraint (tmpc, rhsc));
-
/* May return arguments. */
for (k = 0; k < gimple_call_num_args (stmt); ++k)
{
--- 3580,3602 ----
const function that returns a pointer in the statement STMT. */
static void
! handle_const_call (gimple stmt, VEC(ce_s, heap) **results)
{
! struct constraint_expr rhsc, tmpc;
! tree tmpvar = NULL_TREE;
! unsigned int k;
! /* Treat nested const functions the same as pure functions as far
! as the static chain is concerned. */
if (gimple_call_chain (stmt))
{
! make_constraint_to (callused_id, gimple_call_chain (stmt));
! rhsc.var = callused_id;
rhsc.offset = 0;
! rhsc.type = SCALAR;
! VEC_safe_push (ce_s, heap, *results, &rhsc);
}
/* May return arguments. */
for (k = 0; k < gimple_call_num_args (stmt); ++k)
{
*************** handle_const_call (gimple stmt)
*** 3606,3631 ****
struct constraint_expr *argp;
int i;
get_constraint_for (arg, &argc);
for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++)
process_constraint (new_constraint (tmpc, *argp));
VEC_free (ce_s, heap, argc);
}
}
! for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
! process_constraint (new_constraint (*lhsp, tmpc));
!
! VEC_free (ce_s, heap, lhsc);
}
/* For non-IPA mode, generate constraints necessary for a call to a
pure function in statement STMT. */
static void
! handle_pure_call (gimple stmt)
{
unsigned i;
/* Memory reached from pointer arguments is call-used. */
for (i = 0; i < gimple_call_num_args (stmt); ++i)
--- 3608,3648 ----
struct constraint_expr *argp;
int i;
+ /* We always use a temporary here, otherwise we end up with
+ a quadratic amount of constraints for
+ large_struct = const_call (large_struct);
+ with field-sensitive PTA. */
+ if (tmpvar == NULL_TREE)
+ {
+ tmpvar = create_tmp_var_raw (ptr_type_node, "consttmp");
+ tmpc = get_constraint_exp_for_temp (tmpvar);
+ }
+
get_constraint_for (arg, &argc);
for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++)
process_constraint (new_constraint (tmpc, *argp));
VEC_free (ce_s, heap, argc);
}
}
+ if (tmpvar != NULL_TREE)
+ VEC_safe_push (ce_s, heap, *results, &tmpc);
! /* May return addresses of globals. */
! rhsc.var = nonlocal_id;
! rhsc.offset = 0;
! rhsc.type = ADDRESSOF;
! VEC_safe_push (ce_s, heap, *results, &rhsc);
}
/* For non-IPA mode, generate constraints necessary for a call to a
pure function in statement STMT. */
static void
! handle_pure_call (gimple stmt, VEC(ce_s, heap) **results)
{
+ struct constraint_expr rhsc;
unsigned i;
+ bool need_callused = false;
/* Memory reached from pointer arguments is call-used. */
for (i = 0; i < gimple_call_num_args (stmt); ++i)
*************** handle_pure_call (gimple stmt)
*** 3633,3680 ****
tree arg = gimple_call_arg (stmt, i);
if (could_have_pointers (arg))
! make_constraint_to (callused_id, arg);
}
/* The static chain is used as well. */
if (gimple_call_chain (stmt))
- make_constraint_to (callused_id, gimple_call_chain (stmt));
-
- /* If the call returns a pointer it may point to reachable memory
- from the arguments. Not so for malloc functions though. */
- if (gimple_call_lhs (stmt)
- && could_have_pointers (gimple_call_lhs (stmt))
- && !(gimple_call_flags (stmt) & ECF_MALLOC))
{
! tree lhs = gimple_call_lhs (stmt);
! VEC(ce_s, heap) *lhsc = NULL;
! struct constraint_expr rhsc;
! struct constraint_expr *lhsp;
! unsigned j;
!
! get_constraint_for (lhs, &lhsc);
!
! /* If this is a nested function then it can return anything. */
! if (gimple_call_chain (stmt))
! {
! rhsc.var = anything_id;
! rhsc.offset = 0;
! rhsc.type = ADDRESSOF;
! for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
! process_constraint (new_constraint (*lhsp, rhsc));
! VEC_free (ce_s, heap, lhsc);
! return;
! }
! /* Else just add the call-used memory here. Escaped variables
! and globals will be dealt with in handle_lhs_call. */
rhsc.var = callused_id;
rhsc.offset = 0;
rhsc.type = SCALAR;
! for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
! process_constraint (new_constraint (*lhsp, rhsc));
! VEC_free (ce_s, heap, lhsc);
}
}
/* Walk statement T setting up aliasing constraints according to the
--- 3650,3680 ----
tree arg = gimple_call_arg (stmt, i);
if (could_have_pointers (arg))
! {
! make_constraint_to (callused_id, arg);
! need_callused = true;
! }
}
/* The static chain is used as well. */
if (gimple_call_chain (stmt))
{
! make_constraint_to (callused_id, gimple_call_chain (stmt));
! need_callused = true;
! }
! /* Pure functions may return callused and escaped memory. */
! if (need_callused)
! {
rhsc.var = callused_id;
rhsc.offset = 0;
rhsc.type = SCALAR;
! VEC_safe_push (ce_s, heap, *results, &rhsc);
}
+ rhsc.var = escaped_id;
+ rhsc.offset = 0;
+ rhsc.type = ADDRESSOF;
+ VEC_safe_push (ce_s, heap, *results, &rhsc);
}
/* Walk statement T setting up aliasing constraints according to the
*************** find_func_aliases (gimple origt)
*** 3739,3771 ****
{
if (!in_ipa_mode)
{
int flags = gimple_call_flags (t);
/* Const functions can return their arguments and addresses
of global memory but not of escaped memory. */
! if (flags & ECF_CONST)
{
if (gimple_call_lhs (t)
&& could_have_pointers (gimple_call_lhs (t)))
! handle_const_call (t);
}
/* Pure functions can return addresses in and of memory
reachable from their arguments, but they are not an escape
point for reachable memory of their arguments. */
! else if (flags & ECF_PURE)
! {
! handle_pure_call (t);
! if (gimple_call_lhs (t)
! && could_have_pointers (gimple_call_lhs (t)))
! handle_lhs_call (gimple_call_lhs (t), flags);
! }
else
! {
! handle_rhs_call (t);
! if (gimple_call_lhs (t)
! && could_have_pointers (gimple_call_lhs (t)))
! handle_lhs_call (gimple_call_lhs (t), flags);
! }
}
else
{
--- 3739,3766 ----
{
if (!in_ipa_mode)
{
+ VEC(ce_s, heap) *rhsc = NULL;
int flags = gimple_call_flags (t);
/* Const functions can return their arguments and addresses
of global memory but not of escaped memory. */
! if (flags & (ECF_CONST|ECF_NOVOPS))
{
if (gimple_call_lhs (t)
&& could_have_pointers (gimple_call_lhs (t)))
! handle_const_call (t, &rhsc);
}
/* Pure functions can return addresses in and of memory
reachable from their arguments, but they are not an escape
point for reachable memory of their arguments. */
! else if (flags & (ECF_PURE|ECF_LOOPING_CONST_OR_PURE))
! handle_pure_call (t, &rhsc);
else
! handle_rhs_call (t, &rhsc);
! if (gimple_call_lhs (t)
! && could_have_pointers (gimple_call_lhs (t)))
! handle_lhs_call (gimple_call_lhs (t), flags, rhsc);
! VEC_free (ce_s, heap, rhsc);
}
else
{
More information about the Gcc-patches
mailing list