tree-ssa-alias.c (ptr_deref_may_alias_decl_p): Handle ADDR_EXPR pointers.

2009-06-19  Richard Guenther  <rguenther@suse.de>

	* tree-ssa-alias.c (ptr_deref_may_alias_decl_p): Handle
	ADDR_EXPR pointers.
	(ptr_derefs_may_alias_p): Likewise.
	(ptr_deref_may_alias_ref_p_1): New function.
	(ptr_deref_may_alias_ref_p): Likewise.
	(ref_maybe_used_by_call_p_1): Handle builtins that are not
	covered by looking at the ESCAPED solution.
	(call_may_clobber_ref_p_1): Likewise.
	* tree-ssa-structalias.c (get_constraint_for_ptr_offset):
	Handle NULL_TREE offset.  Do not produce redundant constraints.
	(process_all_all_constraints): New helper function.
	(do_structure_copy): Use it.
	(handle_lhs_call): Likewise.
	(find_func_aliases): Handle some builtins with pointer arguments
	and/or return values explicitly.

	* gcc.c-torture/execute/20090618-1.c: New testcase.

From-SVN: r148718
This commit is contained in:
Richard Guenther 2009-06-19 16:47:35 +00:00 committed by Richard Biener
parent 217655da6f
commit 779704e7cf
5 changed files with 376 additions and 52 deletions

View File

@ -1,3 +1,21 @@
2009-06-19 Richard Guenther <rguenther@suse.de>
* tree-ssa-alias.c (ptr_deref_may_alias_decl_p): Handle
ADDR_EXPR pointers.
(ptr_derefs_may_alias_p): Likewise.
(ptr_deref_may_alias_ref_p_1): New function.
(ptr_deref_may_alias_ref_p): Likewise.
(ref_maybe_used_by_call_p_1): Handle builtins that are not
covered by looking at the ESCAPED solution.
(call_may_clobber_ref_p_1): Likewise.
* tree-ssa-structalias.c (get_constraint_for_ptr_offset):
Handle NULL_TREE offset. Do not produce redundant constraints.
(process_all_all_constraints): New helper function.
(do_structure_copy): Use it.
(handle_lhs_call): Likewise.
(find_func_aliases): Handle some builtins with pointer arguments
and/or return values explicitly.
2009-06-19 Ian Lance Taylor <iant@google.com>
* varasm.c (const_rtx_hash_1): Remove const qualifier from shift.

View File

@ -1,3 +1,7 @@
2009-06-19 Richard Guenther <rguenther@suse.de>
* gcc.c-torture/execute/20090618-1.c: New testcase.
2009-06-19 Ian Lance Taylor <iant@google.com>
* gcc.dg/Wcxx-compat-17.c: New testcase.

View File

@ -0,0 +1,21 @@
extern void abort (void);
struct X { int *p; int *q; };
int foo(void)
{
int i = 0, j = 1;
struct X x, y;
int **p;
y.p = &i;
x.q = &j;
p = __builtin_mempcpy (&x, &y, sizeof (int *));
return **p;
}
int main()
{
if (foo() != 1)
abort ();
return 0;
}

View File

@ -168,14 +168,9 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
{
struct ptr_info_def *pi;
/* ??? During SCCVN/PRE we can end up with *&x during valueizing
operands. Likewise we can end up with dereferencing constant
pointers. Just bail out in these cases for now. */
if (TREE_CODE (ptr) == ADDR_EXPR
|| TREE_CODE (ptr) == INTEGER_CST)
return true;
gcc_assert (TREE_CODE (ptr) == SSA_NAME
gcc_assert ((TREE_CODE (ptr) == SSA_NAME
|| TREE_CODE (ptr) == ADDR_EXPR
|| TREE_CODE (ptr) == INTEGER_CST)
&& (TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == PARM_DECL
|| TREE_CODE (decl) == RESULT_DECL));
@ -184,6 +179,29 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
if (!may_be_aliased (decl))
return false;
/* ADDR_EXPR pointers either just offset another pointer or directly
specify the pointed-to set. */
if (TREE_CODE (ptr) == ADDR_EXPR)
{
tree base = get_base_address (TREE_OPERAND (ptr, 0));
if (base
&& INDIRECT_REF_P (base))
ptr = TREE_OPERAND (base, 0);
else if (base
&& SSA_VAR_P (base))
return operand_equal_p (base, decl, 0);
else if (base
&& CONSTANT_CLASS_P (base))
return false;
else
return true;
}
/* We can end up with dereferencing constant pointers.
Just bail out in this case. */
if (TREE_CODE (ptr) == INTEGER_CST)
return true;
/* If we do not have useful points-to information for this pointer
we cannot disambiguate anything else. */
pi = SSA_NAME_PTR_INFO (ptr);
@ -202,18 +220,46 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
{
struct ptr_info_def *pi1, *pi2;
/* ??? During SCCVN/PRE we can end up with *&x during valueizing
operands. Likewise we can end up with dereferencing constant
pointers. Just bail out in these cases for now. */
if (TREE_CODE (ptr1) == ADDR_EXPR
|| TREE_CODE (ptr1) == INTEGER_CST
|| TREE_CODE (ptr2) == ADDR_EXPR
gcc_assert ((TREE_CODE (ptr1) == SSA_NAME
|| TREE_CODE (ptr1) == ADDR_EXPR
|| TREE_CODE (ptr1) == INTEGER_CST)
&& (TREE_CODE (ptr2) == SSA_NAME
|| TREE_CODE (ptr2) == ADDR_EXPR
|| TREE_CODE (ptr2) == INTEGER_CST));
/* ADDR_EXPR pointers either just offset another pointer or directly
specify the pointed-to set. */
if (TREE_CODE (ptr1) == ADDR_EXPR)
{
tree base = get_base_address (TREE_OPERAND (ptr1, 0));
if (base
&& INDIRECT_REF_P (base))
ptr1 = TREE_OPERAND (base, 0);
else if (base
&& SSA_VAR_P (base))
return ptr_deref_may_alias_decl_p (ptr2, base);
else
return true;
}
if (TREE_CODE (ptr2) == ADDR_EXPR)
{
tree base = get_base_address (TREE_OPERAND (ptr2, 0));
if (base
&& INDIRECT_REF_P (base))
ptr2 = TREE_OPERAND (base, 0);
else if (base
&& SSA_VAR_P (base))
return ptr_deref_may_alias_decl_p (ptr1, base);
else
return true;
}
/* We can end up with dereferencing constant pointers.
Just bail out in this case. */
if (TREE_CODE (ptr1) == INTEGER_CST
|| TREE_CODE (ptr2) == INTEGER_CST)
return true;
gcc_assert (TREE_CODE (ptr1) == SSA_NAME
&& TREE_CODE (ptr2) == SSA_NAME);
/* We may end up with two empty points-to solutions for two same pointers.
In this case we still want to say both pointers alias, so shortcut
that here. */
@ -232,6 +278,31 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
return pt_solutions_intersect (&pi1->pt, &pi2->pt);
}
/* Return true if dereferencing PTR may alias *REF.
The caller is responsible for applying TBAA to see if PTR
may access *REF at all. */
static bool
ptr_deref_may_alias_ref_p_1 (tree ptr, ao_ref *ref)
{
tree base = ao_ref_base (ref);
if (INDIRECT_REF_P (base))
return ptr_derefs_may_alias_p (ptr, TREE_OPERAND (base, 0));
else if (SSA_VAR_P (base))
return ptr_deref_may_alias_decl_p (ptr, base);
return true;
}
static bool
ptr_deref_may_alias_ref_p (tree ptr, tree ref)
{
ao_ref r;
ao_ref_init (&r, ref);
return ptr_deref_may_alias_ref_p_1 (ptr, &r);
}
/* Dump alias information on FILE. */
@ -778,7 +849,7 @@ refs_output_dependent_p (tree store1, tree store2)
static bool
ref_maybe_used_by_call_p_1 (gimple call, tree ref)
{
tree base;
tree base, callee;
unsigned i;
int flags = gimple_call_flags (call);
@ -803,13 +874,41 @@ ref_maybe_used_by_call_p_1 (gimple call, tree ref)
&& !is_global_var (base))
goto process_args;
callee = gimple_call_fndecl (call);
/* Handle those builtin functions explicitly that do not act as
escape points. See tree-ssa-structalias.c:find_func_aliases
for the list of builtins we might need to handle here. */
if (callee != NULL_TREE
&& DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (callee))
{
/* All the following functions clobber memory pointed to by
their first argument. */
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 src = gimple_call_arg (call, 1);
return ptr_deref_may_alias_ref_p (src, ref);
}
default:
/* Fallthru to general call handling. */;
}
/* Check if base is a global static variable that is not read
by the function. */
if (TREE_CODE (base) == VAR_DECL
&& TREE_STATIC (base)
&& !TREE_PUBLIC (base))
{
tree callee = gimple_call_fndecl (call);
bitmap not_read;
if (callee != NULL_TREE
@ -901,6 +1000,7 @@ static bool
call_may_clobber_ref_p_1 (gimple call, ao_ref *ref)
{
tree base;
tree callee;
/* If the call is pure or const it cannot clobber anything. */
if (gimple_call_flags (call)
@ -926,18 +1026,87 @@ call_may_clobber_ref_p_1 (gimple call, ao_ref *ref)
|| !is_global_var (base)))
return false;
callee = gimple_call_fndecl (call);
/* Handle those builtin functions explicitly that do not act as
escape points. See tree-ssa-structalias.c:find_func_aliases
for the list of builtins we might need to handle here. */
if (callee != NULL_TREE
&& DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (callee))
{
/* All the following functions clobber memory pointed to by
their first argument. */
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 (call, 0);
return ptr_deref_may_alias_ref_p_1 (dest, ref);
}
/* Freeing memory kills the pointed-to memory. More importantly
the call has to serve as a barrier for moving loads and stores
across it. Same is true for memset. */
case BUILT_IN_FREE:
case BUILT_IN_MEMSET:
{
tree ptr = gimple_call_arg (call, 0);
return ptr_deref_may_alias_ref_p_1 (ptr, ref);
}
case BUILT_IN_FREXP:
case BUILT_IN_FREXPF:
case BUILT_IN_FREXPL:
case BUILT_IN_GAMMA_R:
case BUILT_IN_GAMMAF_R:
case BUILT_IN_GAMMAL_R:
case BUILT_IN_LGAMMA_R:
case BUILT_IN_LGAMMAF_R:
case BUILT_IN_LGAMMAL_R:
case BUILT_IN_MODF:
case BUILT_IN_MODFF:
case BUILT_IN_MODFL:
{
tree out = gimple_call_arg (call, 1);
return ptr_deref_may_alias_ref_p_1 (out, ref);
}
case BUILT_IN_REMQUO:
case BUILT_IN_REMQUOF:
case BUILT_IN_REMQUOL:
{
tree out = gimple_call_arg (call, 2);
return ptr_deref_may_alias_ref_p_1 (out, ref);
}
case BUILT_IN_SINCOS:
case BUILT_IN_SINCOSF:
case BUILT_IN_SINCOSL:
{
tree sin = gimple_call_arg (call, 1);
tree cos = gimple_call_arg (call, 2);
return (ptr_deref_may_alias_ref_p_1 (sin, ref)
|| ptr_deref_may_alias_ref_p_1 (cos, ref));
}
default:
/* Fallthru to general call handling. */;
}
/* Check if base is a global static variable that is not written
by the function. */
if (TREE_CODE (base) == VAR_DECL
if (callee != NULL_TREE
&& TREE_CODE (base) == VAR_DECL
&& TREE_STATIC (base)
&& !TREE_PUBLIC (base))
{
tree callee = gimple_call_fndecl (call);
bitmap not_written;
if (callee != NULL_TREE
&& (not_written
= ipa_reference_get_not_written_global (cgraph_node (callee)))
if ((not_written
= ipa_reference_get_not_written_global (cgraph_node (callee)))
&& bitmap_bit_p (not_written, DECL_UID (base)))
return false;
}

View File

@ -2857,7 +2857,8 @@ get_constraint_for_ptr_offset (tree ptr, tree offset,
in a HOST_WIDE_INT, we have to fall back to a conservative
solution which includes all sub-fields of all pointed-to
variables of ptr. */
if (!host_integerp (offset, 0))
if (offset == NULL_TREE
|| !host_integerp (offset, 0))
rhsoffset = UNKNOWN_OFFSET;
else
{
@ -2896,7 +2897,8 @@ get_constraint_for_ptr_offset (tree ptr, tree offset,
c2.var = temp->id;
c2.type = ADDRESSOF;
c2.offset = 0;
VEC_safe_push (ce_s, heap, *results, &c2);
if (c2.var != c->var)
VEC_safe_push (ce_s, heap, *results, &c2);
temp = temp->next;
}
while (temp);
@ -3239,6 +3241,37 @@ get_constraint_for (tree t, VEC (ce_s, heap) **results)
get_constraint_for_1 (t, results, false);
}
/* Efficiently generates constraints from all entries in *RHSC to all
entries in *LHSC. */
static void
process_all_all_constraints (VEC (ce_s, heap) *lhsc, VEC (ce_s, heap) *rhsc)
{
struct constraint_expr *lhsp, *rhsp;
unsigned i, j;
if (VEC_length (ce_s, lhsc) <= 1
|| VEC_length (ce_s, rhsc) <= 1)
{
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));
}
else
{
struct constraint_expr tmp;
tree tmpvar = create_tmp_var_raw (ptr_type_node, "allallcopytmp");
tmp.var = get_vi_for_tree (tmpvar)->id;
tmp.type = SCALAR;
tmp.offset = 0;
for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); ++i)
process_constraint (new_constraint (tmp, *rhsp));
for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); ++i)
process_constraint (new_constraint (*lhsp, tmp));
}
}
/* Handle aggregate copies by expanding into copies of the respective
fields of the structures. */
@ -3256,18 +3289,7 @@ do_structure_copy (tree lhsop, tree rhsop)
if (lhsp->type == DEREF
|| (lhsp->type == ADDRESSOF && lhsp->var == anything_id)
|| rhsp->type == DEREF)
{
struct constraint_expr tmp;
tree tmpvar = create_tmp_var_raw (ptr_type_node,
"structcopydereftmp");
tmp.var = get_vi_for_tree (tmpvar)->id;
tmp.type = SCALAR;
tmp.offset = 0;
for (j = 0; VEC_iterate (ce_s, rhsc, j, rhsp); ++j)
process_constraint (new_constraint (tmp, *rhsp));
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); ++j)
process_constraint (new_constraint (*lhsp, tmp));
}
process_all_all_constraints (lhsc, rhsc);
else if (lhsp->type == SCALAR
&& (rhsp->type == SCALAR
|| rhsp->type == ADDRESSOF))
@ -3426,8 +3448,6 @@ handle_lhs_call (tree lhs, int flags, VEC(ce_s, heap) *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);
@ -3441,9 +3461,7 @@ handle_lhs_call (tree lhs, int flags, VEC(ce_s, heap) *rhsc)
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));
process_all_all_constraints (lhsc, rhsc);
}
VEC_free (ce_s, heap, lhsc);
}
@ -3608,6 +3626,108 @@ find_func_aliases (gimple origt)
pointer passed by address. */
else if (is_gimple_call (t))
{
tree fndecl;
if ((fndecl = gimple_call_fndecl (t)) != NULL_TREE
&& DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
/* ??? All builtins that are handled here need to be handled
in the alias-oracle query functions explicitly! */
switch (DECL_FUNCTION_CODE (fndecl))
{
/* All the following functions return a pointer to the same object
as their first argument points to. The functions do not add
to the ESCAPED solution. The functions make the first argument
pointed to memory point to what the second argument pointed to
memory points to. */
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 res = gimple_call_lhs (t);
tree dest = gimple_call_arg (t, 0);
tree src = gimple_call_arg (t, 1);
if (res != NULL_TREE)
{
get_constraint_for (res, &lhsc);
if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMPCPY
|| DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STPCPY
|| DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STPNCPY)
get_constraint_for_ptr_offset (dest, NULL_TREE, &rhsc);
else
get_constraint_for (dest, &rhsc);
process_all_all_constraints (lhsc, rhsc);
VEC_free (ce_s, heap, lhsc);
VEC_free (ce_s, heap, rhsc);
}
get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
get_constraint_for_ptr_offset (src, NULL_TREE, &rhsc);
do_deref (&lhsc);
do_deref (&rhsc);
process_all_all_constraints (lhsc, rhsc);
VEC_free (ce_s, heap, lhsc);
VEC_free (ce_s, heap, rhsc);
return;
}
case BUILT_IN_MEMSET:
{
tree res = gimple_call_lhs (t);
tree dest = gimple_call_arg (t, 0);
unsigned i;
ce_s *lhsp;
struct constraint_expr ac;
if (res != NULL_TREE)
{
get_constraint_for (res, &lhsc);
get_constraint_for (dest, &rhsc);
process_all_all_constraints (lhsc, rhsc);
VEC_free (ce_s, heap, lhsc);
VEC_free (ce_s, heap, rhsc);
}
get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
do_deref (&lhsc);
ac.type = SCALAR;
ac.var = integer_id;
ac.offset = 0;
for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); ++i)
process_constraint (new_constraint (*lhsp, ac));
VEC_free (ce_s, heap, lhsc);
return;
}
/* All the following functions do not return pointers, do not
modify the points-to sets of memory reachable from their
arguments and do not add to the ESCAPED solution. */
case BUILT_IN_SINCOS:
case BUILT_IN_SINCOSF:
case BUILT_IN_SINCOSL:
case BUILT_IN_FREXP:
case BUILT_IN_FREXPF:
case BUILT_IN_FREXPL:
case BUILT_IN_GAMMA_R:
case BUILT_IN_GAMMAF_R:
case BUILT_IN_GAMMAL_R:
case BUILT_IN_LGAMMA_R:
case BUILT_IN_LGAMMAF_R:
case BUILT_IN_LGAMMAL_R:
case BUILT_IN_MODF:
case BUILT_IN_MODFF:
case BUILT_IN_MODFL:
case BUILT_IN_REMQUO:
case BUILT_IN_REMQUOF:
case BUILT_IN_REMQUOL:
case BUILT_IN_FREE:
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. */;
}
if (!in_ipa_mode)
{
VEC(ce_s, heap) *rhsc = NULL;
@ -3724,7 +3844,6 @@ find_func_aliases (gimple origt)
do_structure_copy (lhsop, rhsop);
else
{
unsigned int j;
struct constraint_expr temp;
get_constraint_for (lhsop, &lhsc);
@ -3743,14 +3862,7 @@ find_func_aliases (gimple origt)
temp.offset = 0;
VEC_safe_push (ce_s, heap, rhsc, &temp);
}
for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
{
struct constraint_expr *c2;
unsigned int k;
for (k = 0; VEC_iterate (ce_s, rhsc, k, c2); k++)
process_constraint (new_constraint (*c, *c2));
}
process_all_all_constraints (lhsc, rhsc);
}
/* If there is a store to a global variable the rhs escapes. */
if ((lhsop = get_base_address (lhsop)) != NULL_TREE