diff --git a/gcc/ChangeLog b/gcc/ChangeLog index cb34bee551e..f4e7816bb45 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2009-06-19 Richard Guenther + + * 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 * varasm.c (const_rtx_hash_1): Remove const qualifier from shift. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index fec4369b18a..e1ea0a3385c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2009-06-19 Richard Guenther + + * gcc.c-torture/execute/20090618-1.c: New testcase. + 2009-06-19 Ian Lance Taylor * gcc.dg/Wcxx-compat-17.c: New testcase. diff --git a/gcc/testsuite/gcc.c-torture/execute/20090618-1.c b/gcc/testsuite/gcc.c-torture/execute/20090618-1.c new file mode 100644 index 00000000000..f522116eba1 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/20090618-1.c @@ -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; +} diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index 6cbec5de2bf..c83488b1788 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -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; } diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index 60863d560f5..bca21457560 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -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