Commit 472c7fbd by Richard Guenther Committed by Richard Biener

re PR tree-optimization/39120 (Missed escape constraints for call results)

2009-03-27  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.

2009-03-27  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.

From-SVN: r145137
parent d9223014
2009-03-27 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.
2009-03-27 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.
2009-03-27 H.J. Lu <hongjiu.lu@intel.com> 2009-03-27 H.J. Lu <hongjiu.lu@intel.com>
PR c/39323 PR c/39323
......
2009-03-27 Richard Guenther <rguenther@suse.de>
PR tree-optimization/39120
* gcc.c-torture/execute/pr39120.c: New testcase.
2009-03-27 Richard Guenther <rguenther@suse.de>
PR tree-optimization/39120
* gcc.dg/torture/pta-callused-1.c: New testcase.
2009-03-27 H.J. Lu <hongjiu.lu@intel.com> 2009-03-27 H.J. Lu <hongjiu.lu@intel.com>
PR c/39323 PR c/39323
......
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;
}
/* { 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" } } */
...@@ -1592,12 +1592,9 @@ do_sd_constraint (constraint_graph_t graph, constraint_t c, ...@@ -1592,12 +1592,9 @@ do_sd_constraint (constraint_graph_t graph, constraint_t c,
if (get_varinfo (t)->is_special_var) if (get_varinfo (t)->is_special_var)
flag |= bitmap_ior_into (sol, get_varinfo (t)->solution); flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
/* Merging the solution from ESCAPED needlessly increases /* Merging the solution from ESCAPED needlessly increases
the set. Use ESCAPED as representative instead. the set. Use ESCAPED as representative instead. */
Same for CALLUSED. */
else if (get_varinfo (t)->id == find (escaped_id)) else if (get_varinfo (t)->id == find (escaped_id))
flag |= bitmap_set_bit (sol, 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)) else if (add_graph_edge (graph, lhs, t))
flag |= bitmap_ior_into (sol, get_varinfo (t)->solution); flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
} }
...@@ -2516,9 +2513,8 @@ solve_graph (constraint_graph_t graph) ...@@ -2516,9 +2513,8 @@ solve_graph (constraint_graph_t graph)
solution_empty = bitmap_empty_p (solution); solution_empty = bitmap_empty_p (solution);
if (!solution_empty if (!solution_empty
/* Do not propagate the ESCAPED/CALLUSED solutions. */ /* Do not propagate the ESCAPED solutions. */
&& i != find (escaped_id) && i != find (escaped_id))
&& i != find (callused_id))
{ {
bitmap_iterator bi; bitmap_iterator bi;
...@@ -3488,8 +3484,9 @@ make_escape_constraint (tree op) ...@@ -3488,8 +3484,9 @@ make_escape_constraint (tree op)
RHS. */ RHS. */
static void static void
handle_rhs_call (gimple stmt) handle_rhs_call (gimple stmt, VEC(ce_s, heap) **results)
{ {
struct constraint_expr rhsc;
unsigned i; unsigned i;
for (i = 0; i < gimple_call_num_args (stmt); ++i) for (i = 0; i < gimple_call_num_args (stmt); ++i)
...@@ -3505,6 +3502,12 @@ handle_rhs_call (gimple stmt) ...@@ -3505,6 +3502,12 @@ handle_rhs_call (gimple stmt)
/* The static chain escapes as well. */ /* The static chain escapes as well. */
if (gimple_call_chain (stmt)) if (gimple_call_chain (stmt))
make_escape_constraint (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 /* For non-IPA mode, generate constraints necessary for a call
...@@ -3512,10 +3515,9 @@ handle_rhs_call (gimple stmt) ...@@ -3512,10 +3515,9 @@ handle_rhs_call (gimple stmt)
the LHS point to global and escaped variables. */ the LHS point to global and escaped variables. */
static void static void
handle_lhs_call (tree lhs, int flags) handle_lhs_call (tree lhs, int flags, VEC(ce_s, heap) *rhsc)
{ {
VEC(ce_s, heap) *lhsc = NULL; VEC(ce_s, heap) *lhsc = NULL;
struct constraint_expr rhsc;
unsigned int j; unsigned int j;
struct constraint_expr *lhsp; struct constraint_expr *lhsp;
...@@ -3523,6 +3525,7 @@ handle_lhs_call (tree lhs, int flags) ...@@ -3523,6 +3525,7 @@ handle_lhs_call (tree lhs, int flags)
if (flags & ECF_MALLOC) if (flags & ECF_MALLOC)
{ {
struct constraint_expr rhsc;
tree heapvar = heapvar_lookup (lhs); tree heapvar = heapvar_lookup (lhs);
varinfo_t vi; varinfo_t vi;
...@@ -3546,15 +3549,30 @@ handle_lhs_call (tree lhs, int flags) ...@@ -3546,15 +3549,30 @@ handle_lhs_call (tree lhs, int flags)
vi->size = ~0; vi->size = ~0;
rhsc.type = ADDRESSOF; rhsc.type = ADDRESSOF;
rhsc.offset = 0; rhsc.offset = 0;
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
process_constraint (new_constraint (*lhsp, rhsc));
} }
else else if (VEC_length (ce_s, rhsc) > 0)
{ {
rhsc.var = escaped_id; struct constraint_expr *lhsp, *rhsp;
rhsc.offset = 0; unsigned int i, j;
rhsc.type = ADDRESSOF; /* 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));
} }
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
process_constraint (new_constraint (*lhsp, rhsc));
VEC_free (ce_s, heap, lhsc); VEC_free (ce_s, heap, lhsc);
} }
...@@ -3562,43 +3580,23 @@ handle_lhs_call (tree lhs, int flags) ...@@ -3562,43 +3580,23 @@ handle_lhs_call (tree lhs, int flags)
const function that returns a pointer in the statement STMT. */ const function that returns a pointer in the statement STMT. */
static void static void
handle_const_call (gimple stmt) handle_const_call (gimple stmt, VEC(ce_s, heap) **results)
{ {
tree lhs = gimple_call_lhs (stmt); struct constraint_expr rhsc, tmpc;
VEC(ce_s, heap) *lhsc = NULL; tree tmpvar = NULL_TREE;
struct constraint_expr rhsc; unsigned int k;
unsigned int j, k;
struct constraint_expr *lhsp;
tree tmpvar;
struct constraint_expr tmpc;
get_constraint_for (lhs, &lhsc); /* Treat nested const functions the same as pure functions as far
as the static chain is concerned. */
/* If this is a nested function then it can return anything. */
if (gimple_call_chain (stmt)) if (gimple_call_chain (stmt))
{ {
rhsc.var = anything_id; make_constraint_to (callused_id, gimple_call_chain (stmt));
rhsc.var = callused_id;
rhsc.offset = 0; rhsc.offset = 0;
rhsc.type = ADDRESSOF; rhsc.type = SCALAR;
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++) VEC_safe_push (ce_s, heap, *results, &rhsc);
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. */ /* May return arguments. */
for (k = 0; k < gimple_call_num_args (stmt); ++k) for (k = 0; k < gimple_call_num_args (stmt); ++k)
{ {
...@@ -3610,26 +3608,41 @@ handle_const_call (gimple stmt) ...@@ -3610,26 +3608,41 @@ handle_const_call (gimple stmt)
struct constraint_expr *argp; struct constraint_expr *argp;
int i; 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); get_constraint_for (arg, &argc);
for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++) for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++)
process_constraint (new_constraint (tmpc, *argp)); process_constraint (new_constraint (tmpc, *argp));
VEC_free (ce_s, heap, argc); VEC_free (ce_s, heap, argc);
} }
} }
if (tmpvar != NULL_TREE)
VEC_safe_push (ce_s, heap, *results, &tmpc);
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++) /* May return addresses of globals. */
process_constraint (new_constraint (*lhsp, tmpc)); rhsc.var = nonlocal_id;
rhsc.offset = 0;
VEC_free (ce_s, heap, lhsc); rhsc.type = ADDRESSOF;
VEC_safe_push (ce_s, heap, *results, &rhsc);
} }
/* For non-IPA mode, generate constraints necessary for a call to a /* For non-IPA mode, generate constraints necessary for a call to a
pure function in statement STMT. */ pure function in statement STMT. */
static void static void
handle_pure_call (gimple stmt) handle_pure_call (gimple stmt, VEC(ce_s, heap) **results)
{ {
struct constraint_expr rhsc;
unsigned i; unsigned i;
bool need_callused = false;
/* Memory reached from pointer arguments is call-used. */ /* Memory reached from pointer arguments is call-used. */
for (i = 0; i < gimple_call_num_args (stmt); ++i) for (i = 0; i < gimple_call_num_args (stmt); ++i)
...@@ -3637,48 +3650,31 @@ handle_pure_call (gimple stmt) ...@@ -3637,48 +3650,31 @@ handle_pure_call (gimple stmt)
tree arg = gimple_call_arg (stmt, i); tree arg = gimple_call_arg (stmt, i);
if (could_have_pointers (arg)) if (could_have_pointers (arg))
make_constraint_to (callused_id, arg); {
make_constraint_to (callused_id, arg);
need_callused = true;
}
} }
/* The static chain is used as well. */ /* The static chain is used as well. */
if (gimple_call_chain (stmt)) 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); make_constraint_to (callused_id, gimple_call_chain (stmt));
VEC(ce_s, heap) *lhsc = NULL; need_callused = true;
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 /* Pure functions may return callused and escaped memory. */
and globals will be dealt with in handle_lhs_call. */ if (need_callused)
{
rhsc.var = callused_id; rhsc.var = callused_id;
rhsc.offset = 0; rhsc.offset = 0;
rhsc.type = ADDRESSOF; rhsc.type = SCALAR;
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++) VEC_safe_push (ce_s, heap, *results, &rhsc);
process_constraint (new_constraint (*lhsp, rhsc));
VEC_free (ce_s, heap, lhsc);
} }
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 /* Walk statement T setting up aliasing constraints according to the
...@@ -3743,33 +3739,28 @@ find_func_aliases (gimple origt) ...@@ -3743,33 +3739,28 @@ find_func_aliases (gimple origt)
{ {
if (!in_ipa_mode) if (!in_ipa_mode)
{ {
VEC(ce_s, heap) *rhsc = NULL;
int flags = gimple_call_flags (t); int flags = gimple_call_flags (t);
/* Const functions can return their arguments and addresses /* Const functions can return their arguments and addresses
of global memory but not of escaped memory. */ of global memory but not of escaped memory. */
if (flags & ECF_CONST) if (flags & (ECF_CONST|ECF_NOVOPS))
{ {
if (gimple_call_lhs (t) if (gimple_call_lhs (t)
&& could_have_pointers (gimple_call_lhs (t))) && could_have_pointers (gimple_call_lhs (t)))
handle_const_call (t); handle_const_call (t, &rhsc);
} }
/* Pure functions can return addresses in and of memory /* Pure functions can return addresses in and of memory
reachable from their arguments, but they are not an escape reachable from their arguments, but they are not an escape
point for reachable memory of their arguments. */ point for reachable memory of their arguments. */
else if (flags & ECF_PURE) else if (flags & (ECF_PURE|ECF_LOOPING_CONST_OR_PURE))
{ handle_pure_call (t, &rhsc);
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 else
{ handle_rhs_call (t, &rhsc);
handle_rhs_call (t); if (gimple_call_lhs (t)
if (gimple_call_lhs (t) && could_have_pointers (gimple_call_lhs (t)))
&& could_have_pointers (gimple_call_lhs (t))) handle_lhs_call (gimple_call_lhs (t), flags, rhsc);
handle_lhs_call (gimple_call_lhs (t), flags); VEC_free (ce_s, heap, rhsc);
}
} }
else else
{ {
...@@ -4925,9 +4916,10 @@ find_what_p_points_to (tree p) ...@@ -4925,9 +4916,10 @@ find_what_p_points_to (tree p)
pi->pt_null = 1; pi->pt_null = 1;
else if (vi->id == anything_id else if (vi->id == anything_id
|| vi->id == nonlocal_id || vi->id == nonlocal_id
|| vi->id == escaped_id || vi->id == escaped_id)
|| vi->id == callused_id)
was_pt_anything = 1; was_pt_anything = 1;
else if (vi->id == callused_id)
gcc_unreachable ();
else if (vi->id == readonly_id) else if (vi->id == readonly_id)
was_pt_anything = 1; was_pt_anything = 1;
else if (vi->id == integer_id) else if (vi->id == integer_id)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment