[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