[PATCH] Handle builtin functions with pointer args/returns in PTA and the alias oracle
Richard Guenther
rguenther@suse.de
Fri Jun 19 17:56:00 GMT 2009
On Thu, 18 Jun 2009, Richard Guenther wrote:
>
> This implements knowledge about builtin functions with pointer arguments
> and return values in points-to analysis as well as in the alias-oracle.
> The effect is that arguments to most string and math functions do not
> escape and that we know what their return values point to. We also
> can restrict clobbering to the relevant arguments.
>
> Bootstrapped and tested on x86_64-unknown-linux-gnu, I'll apply this
> tomorrow to let people time to comment.
>
> I chickened out on all the printf-style stuff due to the usual
> problem of glibc allowing hooks here. If I missed commonly used
> functions other than these please tell me.
Reviewing the patch before committing revealed that I added a
bunch of handling to the wrong function. Fixed and re-tested,
the following is what I committed.
Richard.
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.
Index: gcc/tree-ssa-structalias.c
===================================================================
*** gcc/tree-ssa-structalias.c (revision 148652)
--- gcc/tree-ssa-structalias.c (working copy)
*************** get_constraint_for_ptr_offset (tree ptr,
*** 2857,2863 ****
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))
rhsoffset = UNKNOWN_OFFSET;
else
{
--- 2857,2864 ----
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 (offset == NULL_TREE
! || !host_integerp (offset, 0))
rhsoffset = UNKNOWN_OFFSET;
else
{
*************** get_constraint_for_ptr_offset (tree ptr,
*** 2896,2902 ****
c2.var = temp->id;
c2.type = ADDRESSOF;
c2.offset = 0;
! VEC_safe_push (ce_s, heap, *results, &c2);
temp = temp->next;
}
while (temp);
--- 2897,2904 ----
c2.var = temp->id;
c2.type = ADDRESSOF;
c2.offset = 0;
! if (c2.var != c->var)
! VEC_safe_push (ce_s, heap, *results, &c2);
temp = temp->next;
}
while (temp);
*************** get_constraint_for (tree t, VEC (ce_s, h
*** 3239,3244 ****
--- 3241,3277 ----
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. */
*************** do_structure_copy (tree lhsop, tree rhso
*** 3256,3273 ****
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));
! }
else if (lhsp->type == SCALAR
&& (rhsp->type == SCALAR
|| rhsp->type == ADDRESSOF))
--- 3289,3295 ----
if (lhsp->type == DEREF
|| (lhsp->type == ADDRESSOF && lhsp->var == anything_id)
|| rhsp->type == DEREF)
! process_all_all_constraints (lhsc, rhsc);
else if (lhsp->type == SCALAR
&& (rhsp->type == SCALAR
|| rhsp->type == ADDRESSOF))
*************** handle_lhs_call (tree lhs, int flags, VE
*** 3426,3433 ****
}
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);
--- 3448,3453 ----
*************** handle_lhs_call (tree lhs, int flags, VE
*** 3441,3449 ****
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);
}
--- 3461,3467 ----
tmpc.type = SCALAR;
VEC_safe_push (ce_s, heap, lhsc, &tmpc);
}
! process_all_all_constraints (lhsc, rhsc);
}
VEC_free (ce_s, heap, lhsc);
}
*************** find_func_aliases (gimple origt)
*** 3608,3613 ****
--- 3626,3733 ----
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;
*************** find_func_aliases (gimple origt)
*** 3724,3730 ****
do_structure_copy (lhsop, rhsop);
else
{
- unsigned int j;
struct constraint_expr temp;
get_constraint_for (lhsop, &lhsc);
--- 3844,3849 ----
*************** find_func_aliases (gimple origt)
*** 3743,3756 ****
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));
! }
}
/* If there is a store to a global variable the rhs escapes. */
if ((lhsop = get_base_address (lhsop)) != NULL_TREE
--- 3862,3868 ----
temp.offset = 0;
VEC_safe_push (ce_s, heap, rhsc, &temp);
}
! 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
Index: gcc/testsuite/gcc.c-torture/execute/20090618-1.c
===================================================================
*** gcc/testsuite/gcc.c-torture/execute/20090618-1.c (revision 0)
--- gcc/testsuite/gcc.c-torture/execute/20090618-1.c (revision 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;
+ }
Index: gcc/tree-ssa-alias.c
===================================================================
*** gcc/tree-ssa-alias.c (revision 148703)
--- gcc/tree-ssa-alias.c (working copy)
*************** ptr_deref_may_alias_decl_p (tree ptr, tr
*** 168,181 ****
{
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
&& (TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == PARM_DECL
|| TREE_CODE (decl) == RESULT_DECL));
--- 168,176 ----
{
struct ptr_info_def *pi;
! 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));
*************** ptr_deref_may_alias_decl_p (tree ptr, tr
*** 184,189 ****
--- 179,207 ----
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);
*************** ptr_derefs_may_alias_p (tree ptr1, tree
*** 202,219 ****
{
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
|| 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. */
--- 220,265 ----
{
struct ptr_info_def *pi1, *pi2;
! 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;
/* 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. */
*************** ptr_derefs_may_alias_p (tree ptr1, tree
*** 232,237 ****
--- 278,308 ----
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. */
*************** refs_output_dependent_p (tree store1, tr
*** 778,784 ****
static bool
ref_maybe_used_by_call_p_1 (gimple call, tree ref)
{
! tree base;
unsigned i;
int flags = gimple_call_flags (call);
--- 849,855 ----
static bool
ref_maybe_used_by_call_p_1 (gimple call, tree ref)
{
! tree base, callee;
unsigned i;
int flags = gimple_call_flags (call);
*************** ref_maybe_used_by_call_p_1 (gimple call,
*** 803,815 ****
&& !is_global_var (base))
goto process_args;
/* 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
--- 874,914 ----
&& !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))
{
bitmap not_read;
if (callee != NULL_TREE
*************** static bool
*** 901,906 ****
--- 1000,1006 ----
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)
*************** call_may_clobber_ref_p_1 (gimple call, a
*** 926,943 ****
|| !is_global_var (base)))
return false;
/* Check if base is a global static variable that is not written
by the function. */
! if (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)))
&& bitmap_bit_p (not_written, DECL_UID (base)))
return false;
}
--- 1026,1112 ----
|| !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 (callee != NULL_TREE
! && TREE_CODE (base) == VAR_DECL
&& TREE_STATIC (base)
&& !TREE_PUBLIC (base))
{
bitmap not_written;
! if ((not_written
! = ipa_reference_get_not_written_global (cgraph_node (callee)))
&& bitmap_bit_p (not_written, DECL_UID (base)))
return false;
}
More information about the Gcc-patches
mailing list