public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH][RFC] IPA-PTA and how/where to store the info
@ 2009-11-06 17:38 Richard Guenther
  0 siblings, 0 replies; only message in thread
From: Richard Guenther @ 2009-11-06 17:38 UTC (permalink / raw)
  To: gcc-patches


In the last couple of days I have experimented with making IPA-PTA
do something, do the correct thing and make use of the results.
So the following patch is what I have for the moment - it does have
some rough edges which is why I'd like to hear opinions on the
way forward.

1) Currently I store IPA PTA results in addition to local PTA results
(that should make some statistics possible)

2) With IPA PTA we have to do mod/ref analysis to have call clobber/use
information at call sites.  I store the required information in
the gimple-df structure - but that get's blown away after we output
the function which is before all callers have been processed.  Which
of course makes the excercise useless.  So I stop nuking cfun and
gimple-df as a hack for now.  What I thought of doing is to store
the information in the call statements itself (ugh you say - that's
two points-to sets, 64 bytes).  Any better ideas?

3) Inlining and PTA do not play along very well.  They hate each
other if you do IPA PTA.  This is because inlining duplicates decls
that can be part of pointed-to sets.  My workaround is to introduce
a "original" DECL_UID (named DECL_PT_UID) that does not get altered
when the inliner copies decls, so the points-to sets stay valid.
Of course we loose the extra precision that inlining would give
us (it basically makes some calls context-sensitive).  For IPA PTA
not doing this but trying to fixup things would be very expensive.
The only way would be to commit all inlining decisions and only
after that run IPA PTA.  Which of course wouldn't play very well
with WOPR later (not that IPA PTA would in any way play well with it).

4) I hope that I fixed most wrong-code issues - with IPA PTA we
need to correctly handle cross-function constraints with interesting
effects on things like __builtin_init_trampoline or
__builtin_va_start ... I hope I didn't miss yet another interesting
piece here (well, EH comes to my mind ... ugh).

5) The patch needs some TLC (and a changelog entry).
Enough time until stage1 for that.

Bootstrapped (C only) and tested (C only, with -fipa-pta enabled)
on x86_64-unknown-linux-gnu.  gcc.c-torture/execute/va-arg-22.c fails,
miscompiled by the vectorizer, gcc.target/i386/funcspec-* fail
because of the hack in 2)

Any comments?

Thanks,
Richard.


Index: trunk/gcc/tree-ssa-structalias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-structalias.c	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree-ssa-structalias.c	2009-11-06 17:57:02.000000000 +0100
***************
*** 160,165 ****
--- 160,193 ----
    TODO: We could handle unions, but to be honest, it's probably not
    worth the pain or slowdown.  */
  
+ /* IPA-PTA optimizations possible.
+ 
+    When the indirect function called is ANYTHING we can add disambiguation
+    based on the function signatures (or simply the parameter count which
+    is the varinfo size).  We also do not need to consider functions that
+    do not have their address taken.
+ 
+    The is_global_var bit which marks escape points is overly conservative
+    in IPA mode.  Split it to is_escape_point and is_global_var - only
+    externally visible globals are escape points in IPA mode.
+ 
+    The way we introduce DECL_PT_UID to avoid fixing up all points-to
+    sets in the translation unit when we copy a DECL during inlining
+    pessimizes precision.  This is because the DECL_PT_UIDs are tracked
+    even for early inlining where it would be safe for IPA PTA to
+    distinguish them.  The advantage is that the DECL_PT_UID keeps
+    compile-time and memory usage overhead low - the points-to sets
+    do not grow or get unshared as they would during a fixup phase.
+    An alternative solution is to delay IPA PTA until after all
+    inlining transformations have been applied.
+ 
+    Explicit builtin handling in find_func_clobbers should be
+    extended to pure functions like strcmp to avoid refering to
+    CALLUSED.  Likewise find_func_aliases can be adjusted to avoid
+    using CALLUSED here.
+ 
+    And probably more.  */
+ 
  static GTY ((if_marked ("tree_map_marked_p"), param_is (struct tree_map)))
  htab_t heapvar_for_stmt;
  
*************** struct variable_info
*** 235,240 ****
--- 263,271 ----
    /* True if this represents a global variable.  */
    unsigned int is_global_var : 1;
  
+   /* True if this represents a IPA function info.  */
+   unsigned int is_fn_info : 1;
+ 
    /* A link to the variable for the next field in this structure.  */
    struct variable_info *next;
  
*************** new_var_info (tree t, const char *name)
*** 367,372 ****
--- 398,404 ----
    ret->is_restrict_var = false;
    ret->may_have_pointers = true;
    ret->is_global_var = (t == NULL_TREE);
+   ret->is_fn_info = false;
    if (t && DECL_P (t))
      ret->is_global_var = is_global_var (t);
    ret->solution = BITMAP_ALLOC (&pta_obstack);
*************** make_constraint_from_restrict (varinfo_t
*** 3432,3437 ****
--- 3464,3512 ----
    vi->may_have_pointers = 0;
  }
  
+ /* In IPA mode there are varinfos for different aspects of reach
+    function designator.  One for the points-to set of the return
+    value, one for the variables that are clobbered by the function,
+    one for its uses and one for each parameter (including a single
+    glob for remaining variadic arguments).
+    ???  We miss the static chain and the by-reference result decl here.  */
+ 
+ enum { fi_clobbers = 1, fi_uses = 2,
+        fi_static_chain = 3, fi_result = 4, fi_parm_base = 5 };
+ 
+ /* Get a constraint for the requested part of a function designator FI
+    when operating in IPA mode.  */
+ 
+ static struct constraint_expr
+ get_function_part_constraint (varinfo_t fi, unsigned part)
+ {
+   struct constraint_expr c;
+ 
+   gcc_assert (in_ipa_mode);
+ 
+   if (fi->id == anything_id)
+     {
+       /* ???  We probably should have a ANYFN special variable.  */
+       c.var = anything_id;
+       c.offset = 0;
+       c.type = SCALAR;
+     }
+   else if (TREE_CODE (fi->decl) == FUNCTION_DECL)
+     {
+       c.var = first_vi_for_offset (fi, part)->id;
+       c.offset = 0;
+       c.type = SCALAR;
+     }
+   else
+     {
+       c.var = fi->id;
+       c.offset = part;
+       c.type = DEREF;
+     }
+ 
+   return c;
+ }
+ 
  /* For non-IPA mode, generate constraints necessary for a call on the
     RHS.  */
  
*************** handle_pure_call (gimple stmt, VEC(ce_s,
*** 3605,3610 ****
--- 3680,3719 ----
    VEC_safe_push (ce_s, heap, *results, &rhsc);
  }
  
+ 
+ /* Return the varinfo for the callee of CALL.  */
+ 
+ static varinfo_t
+ get_fi_for_callee (gimple call)
+ {
+   tree decl;
+ 
+   /* If we can directly resolve the function being called, do so.
+      Otherwise, it must be some sort of indirect expression that
+      we should still be able to handle.  */
+   decl = gimple_call_fndecl (call);
+   if (decl)
+     return get_vi_for_tree (decl);
+ 
+   decl = gimple_call_fn (call);
+   /* The function can be either an SSA name pointer or,
+      worse, an OBJ_TYPE_REF.  In this case we have no
+      clue and should be getting ANYFN (well, ANYTHING for now).  */
+   if (TREE_CODE (decl) == SSA_NAME)
+     {
+       if (TREE_CODE (decl) == SSA_NAME
+ 	  && TREE_CODE (SSA_NAME_VAR (decl)) == PARM_DECL
+ 	  && SSA_NAME_IS_DEFAULT_DEF (decl))
+ 	decl = SSA_NAME_VAR (decl);
+       return get_vi_for_tree (decl);
+     }
+   else if (TREE_CODE (decl) == INTEGER_CST
+ 	   || TREE_CODE (decl) == OBJ_TYPE_REF)
+     return get_varinfo (anything_id);
+   else
+     gcc_unreachable ();
+ }
+ 
  /* Walk statement T setting up aliasing constraints according to the
     references found in T.  This function is the main part of the
     constraint builder.  AI points to auxiliary alias information used
*************** find_func_aliases (gimple origt)
*** 3617,3622 ****
--- 3726,3732 ----
    VEC(ce_s, heap) *lhsc = NULL;
    VEC(ce_s, heap) *rhsc = NULL;
    struct constraint_expr *c;
+   varinfo_t fi;
  
    /* Now build constraints expressions.  */
    if (gimple_code (t) == GIMPLE_PHI)
*************** find_func_aliases (gimple origt)
*** 3771,3776 ****
--- 3881,3964 ----
  	  case BUILT_IN_REMQUOL:
  	  case BUILT_IN_FREE:
  	    return;
+ 	  /* Trampolines are special - they set up passing the static
+ 	     frame.  */
+ 	  case BUILT_IN_INIT_TRAMPOLINE:
+ 	    {
+ 	      tree tramp = gimple_call_arg (t, 0);
+ 	      tree nfunc = gimple_call_arg (t, 1);
+ 	      tree frame = gimple_call_arg (t, 2);
+ 	      unsigned i;
+ 	      struct constraint_expr lhs, *rhsp;
+ 	      if (in_ipa_mode)
+ 		{
+ 		  varinfo_t nfi = NULL;
+ 		  gcc_assert (TREE_CODE (nfunc) == ADDR_EXPR);
+ 		  nfi = lookup_vi_for_tree (TREE_OPERAND (nfunc, 0));
+ 		  if (nfi)
+ 		    {
+ 		      lhs = get_function_part_constraint (nfi, fi_static_chain);
+ 		      get_constraint_for (frame, &rhsc);
+ 		      for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); ++i)
+ 			process_constraint (new_constraint (lhs, *rhsp));
+ 		      VEC_free (ce_s, heap, rhsc);
+ 
+ 		      /* Make the frame point to the function for
+ 			 the trampoline adjustment call.  */
+ 		      get_constraint_for (tramp, &lhsc);
+ 		      do_deref (&lhsc);
+ 		      get_constraint_for (nfunc, &rhsc);
+ 		      process_all_all_constraints (lhsc, rhsc);
+ 		      VEC_free (ce_s, heap, rhsc);
+ 		      VEC_free (ce_s, heap, lhsc);
+ 
+ 		      return;
+ 		    }
+ 		}
+ 	      /* Else fallthru to generic handling which will let
+ 	         the frame escape.  */
+ 	      break;
+ 	    }
+ 	  case BUILT_IN_ADJUST_TRAMPOLINE:
+ 	    {
+ 	      tree tramp = gimple_call_arg (t, 0);
+ 	      tree res = gimple_call_lhs (t);
+ 	      if (in_ipa_mode && res)
+ 		{
+ 		  get_constraint_for (res, &lhsc);
+ 		  get_constraint_for (tramp, &rhsc);
+ 		  do_deref (&rhsc);
+ 		  process_all_all_constraints (lhsc, rhsc);
+ 		  VEC_free (ce_s, heap, rhsc);
+ 		  VEC_free (ce_s, heap, lhsc);
+ 		}
+ 	      return;
+ 	    }
+ 	  /* Variadic argument handling needs to be handled in IPA
+ 	     mode as well.  */
+ 	  case BUILT_IN_VA_START:
+ 	    {
+ 	      if (in_ipa_mode)
+ 		{
+ 		  tree valist = gimple_call_arg (t, 0);
+ 		  struct constraint_expr rhs, *lhsp;
+ 		  unsigned i;
+ 		  fi = lookup_vi_for_tree (cfun->decl);
+ 		  gcc_assert (fi != NULL);
+ 		  get_constraint_for (valist, &lhsc);
+ 		  do_deref (&lhsc);
+ 		  rhs = get_function_part_constraint (fi, ~0);
+ 		  rhs.type = ADDRESSOF;
+ 		  for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); ++i)
+ 		    process_constraint (new_constraint (*lhsp, rhs));
+ 		  VEC_free (ce_s, heap, lhsc);
+ 		  return;
+ 		}
+ 	      break;
+ 	    }
+ 	  /* va_end doesn't have any effect that matters.  */
+ 	  case BUILT_IN_VA_END:
+ 	    return;
  	  /* printf-style functions may have hooks to set pointers to
  	     point to somewhere into the generated string.  Leave them
  	     for a later excercise...  */
*************** find_func_aliases (gimple origt)
*** 3807,3838 ****
        else
  	{
  	  tree lhsop;
! 	  varinfo_t fi;
! 	  int i = 1;
! 	  size_t j;
! 	  tree decl;
  
! 	  lhsop = gimple_call_lhs (t);
! 	  decl = gimple_call_fndecl (t);
! 
! 	  /* If we can directly resolve the function being called, do so.
! 	     Otherwise, it must be some sort of indirect expression that
! 	     we should still be able to handle.  */
! 	  if (decl)
! 	    fi = get_vi_for_tree (decl);
! 	  else
! 	    {
! 	      decl = gimple_call_fn (t);
! 	      /* The function can be either an SSA name pointer or,
! 		 worse, an OBJ_TYPE_REF.  In this case we have no
! 		 clue and should be getting ANYFN (well, ANYTHING for now).  */
! 	      if (TREE_CODE (decl) == SSA_NAME)
! 		fi = get_vi_for_tree (decl);
! 	      else if (TREE_CODE (decl) == OBJ_TYPE_REF)
! 		fi = get_varinfo (anything_id);
! 	      else
! 		gcc_unreachable ();
! 	    }
  
  	  /* Assign all the passed arguments to the appropriate incoming
  	     parameters of the function.  */
--- 3995,4003 ----
        else
  	{
  	  tree lhsop;
! 	  unsigned j;
  
! 	  fi = get_fi_for_callee (t);
  
  	  /* Assign all the passed arguments to the appropriate incoming
  	     parameters of the function.  */
*************** find_func_aliases (gimple origt)
*** 3842,3892 ****
  	      struct constraint_expr *rhsp;
  	      tree arg = gimple_call_arg (t, j);
  
  	      get_constraint_for (arg, &rhsc);
! 	      if (TREE_CODE (decl) != FUNCTION_DECL)
! 		{
! 		  lhs.type = DEREF;
! 		  lhs.var = fi->id;
! 		  lhs.offset = i;
! 		}
! 	      else
! 		{
! 		  lhs.type = SCALAR;
! 		  lhs.var = first_vi_for_offset (fi, i)->id;
! 		  lhs.offset = 0;
! 		}
  	      while (VEC_length (ce_s, rhsc) != 0)
  		{
  		  rhsp = VEC_last (ce_s, rhsc);
  		  process_constraint (new_constraint (lhs, *rhsp));
  		  VEC_pop (ce_s, rhsc);
  		}
- 	      i++;
  	    }
  
  	  /* If we are returning a value, assign it to the result.  */
! 	  if (lhsop)
  	    {
  	      struct constraint_expr rhs;
  	      struct constraint_expr *lhsp;
- 	      unsigned int j = 0;
  
  	      get_constraint_for (lhsop, &lhsc);
! 	      if (TREE_CODE (decl) != FUNCTION_DECL)
! 		{
! 		  rhs.type = DEREF;
! 		  rhs.var = fi->id;
! 		  rhs.offset = i;
! 		}
! 	      else
! 		{
! 		  rhs.type = SCALAR;
! 		  rhs.var = first_vi_for_offset (fi, i)->id;
! 		  rhs.offset = 0;
! 		}
  	      for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
  		process_constraint (new_constraint (*lhsp, rhs));
  	    }
  	}
      }
    /* Otherwise, just a regular assignment statement.  Only care about
--- 4007,4050 ----
  	      struct constraint_expr *rhsp;
  	      tree arg = gimple_call_arg (t, j);
  
+ 	      if (!could_have_pointers (arg))
+ 		continue;
+ 
  	      get_constraint_for (arg, &rhsc);
! 	      lhs = get_function_part_constraint (fi, fi_parm_base + j);
  	      while (VEC_length (ce_s, rhsc) != 0)
  		{
  		  rhsp = VEC_last (ce_s, rhsc);
  		  process_constraint (new_constraint (lhs, *rhsp));
  		  VEC_pop (ce_s, rhsc);
  		}
  	    }
  
  	  /* If we are returning a value, assign it to the result.  */
! 	  lhsop = gimple_call_lhs (t);
! 	  if (lhsop
! 	      && could_have_pointers (lhsop))
  	    {
  	      struct constraint_expr rhs;
  	      struct constraint_expr *lhsp;
  
  	      get_constraint_for (lhsop, &lhsc);
! 	      rhs = get_function_part_constraint (fi, fi_result);
  	      for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
  		process_constraint (new_constraint (*lhsp, rhs));
  	    }
+ 
+ 	  /* If we use a static chain, pass it along.  */
+ 	  if (gimple_call_chain (t))
+ 	    {
+ 	      struct constraint_expr lhs;
+ 	      struct constraint_expr *rhsp;
+ 
+ 	      get_constraint_for (gimple_call_chain (t), &rhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_static_chain);
+ 	      for (j = 0; VEC_iterate (ce_s, rhsc, j, rhsp); j++)
+ 		process_constraint (new_constraint (lhs, *rhsp));
+ 	    }
  	}
      }
    /* Otherwise, just a regular assignment statement.  Only care about
*************** find_func_aliases (gimple origt)
*** 3950,3956 ****
  	   && gimple_return_retval (t) != NULL_TREE
  	   && could_have_pointers (gimple_return_retval (t)))
      {
!       make_escape_constraint (gimple_return_retval (t));
      }
    /* Handle asms conservatively by adding escape constraints to everything.  */
    else if (gimple_code (t) == GIMPLE_ASM)
--- 4108,4129 ----
  	   && gimple_return_retval (t) != NULL_TREE
  	   && could_have_pointers (gimple_return_retval (t)))
      {
!       fi = NULL;
!       if (!in_ipa_mode
! 	  || !(fi = get_vi_for_tree (cfun->decl)))
! 	make_escape_constraint (gimple_return_retval (t));
!       else if (in_ipa_mode
! 	       && fi != NULL)
! 	{
! 	  struct constraint_expr lhs ;
! 	  struct constraint_expr *rhsp;
! 	  unsigned i;
! 
! 	  lhs = get_function_part_constraint (fi, fi_result);
! 	  get_constraint_for (gimple_return_retval (t), &rhsc);
! 	  for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); i++)
! 	    process_constraint (new_constraint (lhs, *rhsp));
! 	}
      }
    /* Handle asms conservatively by adding escape constraints to everything.  */
    else if (gimple_code (t) == GIMPLE_ASM)
*************** find_func_aliases (gimple origt)
*** 4019,4024 ****
--- 4192,4469 ----
  }
  
  
+ /* Create a constraint adding to the clobber set of FI the memory
+    pointed to by PTR.  */
+ 
+ static void
+ process_ipa_clobber (varinfo_t fi, tree ptr)
+ {
+   VEC(ce_s, heap) *ptrc = NULL;
+   struct constraint_expr *c, lhs;
+   unsigned i;
+   get_constraint_for (ptr, &ptrc);
+   lhs = get_function_part_constraint (fi, fi_clobbers);
+   for (i = 0; VEC_iterate (ce_s, ptrc, i, c); i++)
+     process_constraint (new_constraint (lhs, *c));
+   VEC_free (ce_s, heap, ptrc);
+ }
+ 
+ /* Walk statement T setting up clobber and use constraints according to the
+    references found in T.  This function is a main part of the
+    IPA constraint builder.  */
+ 
+ static void
+ find_func_clobbers (gimple origt)
+ {
+   gimple t = origt;
+   VEC(ce_s, heap) *lhsc = NULL;
+   VEC(ce_s, heap) *rhsc = NULL;
+   varinfo_t fi;
+ 
+   /* Add constraints for clobbered/used in IPA mode.
+      We are not interested in what automatic variables are clobbered
+      or used as we only use the information in the caller to which
+      they do not escape.  */
+   gcc_assert (in_ipa_mode);
+ 
+   /* If the stmt refers to memory in any way it better had a VUSE.  */
+   if (gimple_vuse (t) == NULL_TREE)
+     return;
+ 
+   /* We'd better have function information for the current function.  */
+   fi = lookup_vi_for_tree (cfun->decl);
+   gcc_assert (fi != NULL);
+ 
+   /* Account for stores in assignments and calls.  */
+   if (gimple_vdef (t) != NULL_TREE
+       && gimple_has_lhs (t))
+     {
+       tree lhs = gimple_get_lhs (t);
+       tree tem = lhs;
+       while (handled_component_p (tem))
+ 	tem = TREE_OPERAND (tem, 0);
+       if ((DECL_P (tem)
+ 	   && !auto_var_in_fn_p (tem, cfun->decl))
+ 	  || INDIRECT_REF_P (tem))
+ 	{
+ 	  struct constraint_expr lhsc, *rhsp;
+ 	  unsigned i;
+ 	  lhsc = get_function_part_constraint (fi, fi_clobbers);
+ 	  get_constraint_for_address_of (lhs, &rhsc);
+ 	  for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); i++)
+ 	    process_constraint (new_constraint (lhsc, *rhsp));
+ 	  VEC_free (ce_s, heap, rhsc);
+ 	}
+     }
+ 
+   /* Account for uses in assigments and returns.  */
+   if (gimple_assign_single_p (t)
+       || (gimple_code (t) == GIMPLE_RETURN
+ 	  && gimple_return_retval (t) != NULL_TREE))
+     {
+       tree rhs = (gimple_assign_single_p (t)
+ 		  ? gimple_assign_rhs1 (t) : gimple_return_retval (t));
+       tree tem = rhs;
+       while (handled_component_p (tem))
+ 	tem = TREE_OPERAND (tem, 0);
+       if ((DECL_P (tem)
+ 	   && !auto_var_in_fn_p (tem, cfun->decl))
+ 	  || INDIRECT_REF_P (tem))
+ 	{
+ 	  struct constraint_expr lhs, *rhsp;
+ 	  unsigned i;
+ 	  lhs = get_function_part_constraint (fi, fi_uses);
+ 	  get_constraint_for_address_of (rhs, &rhsc);
+ 	  for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); i++)
+ 	    process_constraint (new_constraint (lhs, *rhsp));
+ 	  VEC_free (ce_s, heap, rhsc);
+ 	}
+     }
+ 
+   if (is_gimple_call (t))
+     {
+       varinfo_t cfi = NULL;
+       tree decl = gimple_call_fndecl (t);
+       struct constraint_expr lhs, rhs;
+       unsigned i, j;
+ 
+       /* For builtins we do not have separate function info.  For those
+ 	 we do not generate escapes for we have to generate clobbers/uses.  */
+       if (decl
+ 	  && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
+ 	switch (DECL_FUNCTION_CODE (decl))
+ 	  {
+ 	  /* The following functions use and clobber memory pointed to
+ 	     by their arguments.  */
+ 	  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 (t, (DECL_FUNCTION_CODE (decl)
+ 					       == BUILT_IN_BCOPY ? 1 : 0));
+ 	      tree src = gimple_call_arg (t, (DECL_FUNCTION_CODE (decl)
+ 					      == BUILT_IN_BCOPY ? 0 : 1));
+ 	      unsigned i;
+ 	      struct constraint_expr *rhsp, *lhsp;
+ 	      get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_clobbers);
+ 	      for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); i++)
+ 		process_constraint (new_constraint (lhs, *lhsp));
+ 	      VEC_free (ce_s, heap, lhsc);
+ 	      get_constraint_for_ptr_offset (src, NULL_TREE, &rhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_uses);
+ 	      for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); i++)
+ 		process_constraint (new_constraint (lhs, *rhsp));
+ 	      VEC_free (ce_s, heap, rhsc);
+ 	      return;
+ 	    }
+ 	  /* The following function clobbers memory pointed to by
+ 	     its argument.  */
+ 	  case BUILT_IN_MEMSET:
+ 	    {
+ 	      tree dest = gimple_call_arg (t, 0);
+ 	      unsigned i;
+ 	      ce_s *lhsp;
+ 	      get_constraint_for_ptr_offset (dest, NULL_TREE, &lhsc);
+ 	      lhs = get_function_part_constraint (fi, fi_clobbers);
+ 	      for (i = 0; VEC_iterate (ce_s, lhsc, i, lhsp); i++)
+ 		process_constraint (new_constraint (lhs, *lhsp));
+ 	      VEC_free (ce_s, heap, lhsc);
+ 	      return;
+ 	    }
+ 	  /* The following functions clobber their second and third
+ 	     arguments.  */
+ 	  case BUILT_IN_SINCOS:
+ 	  case BUILT_IN_SINCOSF:
+ 	  case BUILT_IN_SINCOSL:
+ 	    {
+ 	      process_ipa_clobber (fi, gimple_call_arg (t, 1));
+ 	      process_ipa_clobber (fi, gimple_call_arg (t, 2));
+ 	      return;
+ 	    }
+ 	  /* The following functions clobber their second argument.  */
+ 	  case BUILT_IN_FREXP:
+ 	  case BUILT_IN_FREXPF:
+ 	  case BUILT_IN_FREXPL:
+ 	  case BUILT_IN_LGAMMA_R:
+ 	  case BUILT_IN_LGAMMAF_R:
+ 	  case BUILT_IN_LGAMMAL_R:
+ 	  case BUILT_IN_GAMMA_R:
+ 	  case BUILT_IN_GAMMAF_R:
+ 	  case BUILT_IN_GAMMAL_R:
+ 	  case BUILT_IN_MODF:
+ 	  case BUILT_IN_MODFF:
+ 	  case BUILT_IN_MODFL:
+ 	    {
+ 	      process_ipa_clobber (fi, gimple_call_arg (t, 1));
+ 	      return;
+ 	    }
+ 	  /* The following functions clobber their third argument.  */
+ 	  case BUILT_IN_REMQUO:
+ 	  case BUILT_IN_REMQUOF:
+ 	  case BUILT_IN_REMQUOL:
+ 	    {
+ 	      process_ipa_clobber (fi, gimple_call_arg (t, 2));
+ 	      return;
+ 	    }
+ 	  /* The following functions neither read nor clobber memory.  */
+ 	  case BUILT_IN_FREE:
+ 	    return;
+ 	  /* Trampolines are of no interest to us.  */
+ 	  case BUILT_IN_INIT_TRAMPOLINE:
+ 	  case BUILT_IN_ADJUST_TRAMPOLINE:
+ 	    return;
+ 	  case BUILT_IN_VA_START:
+ 	  case BUILT_IN_VA_END:
+ 	    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.  */;
+ 	  }
+ 
+       /* Parameters passed by value are used.  */
+       lhs = get_function_part_constraint (fi, fi_uses);
+       for (i = 0; i < gimple_call_num_args (t); i++)
+ 	{
+ 	  struct constraint_expr *rhsp;
+ 	  tree arg = gimple_call_arg (t, i);
+ 
+ 	  if (TREE_CODE (arg) == SSA_NAME
+ 	      || is_gimple_min_invariant (arg))
+ 	    continue;
+ 
+ 	  get_constraint_for_address_of (arg, &rhsc);
+ 	  for (j = 0; VEC_iterate (ce_s, rhsc, j, rhsp); j++)
+ 	    process_constraint (new_constraint (lhs, *rhsp));
+ 	  VEC_free (ce_s, heap, rhsc);
+ 	}
+ 
+       /* Build constraints for propagating clobbers/uses along the
+ 	 callgraph edges.  */
+       cfi = get_fi_for_callee (t);
+       if (cfi->id == anything_id)
+ 	{
+ 	  if (gimple_vdef (t))
+ 	    make_constraint_from (first_vi_for_offset (fi, fi_clobbers),
+ 				  anything_id);
+ 	  make_constraint_from (first_vi_for_offset (fi, fi_uses),
+ 				anything_id);
+ 	  return;
+ 	}
+ 
+       /* For callees without function info (that's external functions),
+ 	 ESCAPED is clobbered and used.  */
+       if (gimple_call_fndecl (t)
+ 	  && !cfi->is_fn_info)
+ 	{
+ 	  if (gimple_vdef (t))
+ 	    make_copy_constraint (first_vi_for_offset (fi, fi_clobbers),
+ 				  escaped_id);
+ 	  make_copy_constraint (first_vi_for_offset (fi, fi_uses), escaped_id);
+ 	  /* ???  CALLUSED?
+ 	     This for example breaks execute/strcmp-1.c.  Thus we need
+ 	     general pure function handling here, better than just
+ 	     using CALLUSED.  */
+ 	  if (gimple_call_flags (t) & (ECF_PURE|ECF_LOOPING_CONST_OR_PURE))
+ 	    make_copy_constraint (first_vi_for_offset (fi, fi_uses),
+ 				  callused_id);
+ 	  return;
+ 	}
+ 
+       /* Otherwise the caller clobbers and uses what the callee does.  */
+       if (gimple_vdef (t))
+ 	{
+ 	  lhs = get_function_part_constraint (fi, fi_clobbers);
+ 	  rhs = get_function_part_constraint (cfi, fi_clobbers);
+ 	  process_constraint (new_constraint (lhs, rhs));
+ 	}
+       lhs = get_function_part_constraint (fi, fi_uses);
+       rhs = get_function_part_constraint (cfi, fi_uses);
+       process_constraint (new_constraint (lhs, rhs));
+     }
+   else if (gimple_code (t) == GIMPLE_ASM)
+     {
+       /* ???  Ick.  We can do better.  */
+       if (gimple_vdef (t))
+ 	make_constraint_from (first_vi_for_offset (fi, fi_clobbers),
+ 			      anything_id);
+       make_constraint_from (first_vi_for_offset (fi, fi_uses),
+ 			    anything_id);
+     }
+ 
+   VEC_free (ce_s, heap, rhsc);
+ }
+ 
+ 
  /* Find the first varinfo in the same variable as START that overlaps with
     OFFSET.  Return NULL if we can't find one.  */
  
*************** first_vi_for_offset (varinfo_t start, un
*** 4041,4047 ****
  	 In that case, however, offset should still be within the size
  	 of the variable. */
        if (offset >= start->offset
! 	  && offset < (start->offset + start->size))
  	return start;
  
        start= start->next;
--- 4486,4492 ----
  	 In that case, however, offset should still be within the size
  	 of the variable. */
        if (offset >= start->offset
! 	  && offset - start->offset < start->size)
  	return start;
  
        start= start->next;
*************** first_or_preceding_vi_for_offset (varinf
*** 4071,4077 ****
       directly preceding offset which may be the last field.  */
    while (start->next
  	 && offset >= start->offset
! 	 && !(offset < (start->offset + start->size)))
      start = start->next;
  
    return start;
--- 4516,4522 ----
       directly preceding offset which may be the last field.  */
    while (start->next
  	 && offset >= start->offset
! 	 && !(offset - start->offset < start->size))
      start = start->next;
  
    return start;
*************** push_fields_onto_fieldstack (tree type,
*** 4295,4315 ****
  static unsigned int
  count_num_arguments (tree decl, bool *is_varargs)
  {
!   unsigned int i = 0;
    tree t;
  
!   for (t = TYPE_ARG_TYPES (TREE_TYPE (decl));
!        t;
!        t = TREE_CHAIN (t))
!     {
!       if (TREE_VALUE (t) == void_type_node)
! 	break;
!       i++;
!     }
! 
    if (!t)
      *is_varargs = true;
!   return i;
  }
  
  /* Creation function node for DECL, using NAME, and return the index
--- 4740,4761 ----
  static unsigned int
  count_num_arguments (tree decl, bool *is_varargs)
  {
!   unsigned int num = 0;
    tree t;
  
!   /* Capture named arguments for K&R functions.  They do not
!      have a prototype and thus no TYPE_ARG_TYPES.  */
!   for (t = DECL_ARGUMENTS (decl); t; t = TREE_CHAIN (t))
!     ++num;
! 
!   /* Check if the function has variadic arguments.  */
!   for (t = TYPE_ARG_TYPES (TREE_TYPE (decl)); t; t = TREE_CHAIN (t))
!     if (TREE_VALUE (t) == void_type_node)
!       break;
    if (!t)
      *is_varargs = true;
! 
!   return num;
  }
  
  /* Creation function node for DECL, using NAME, and return the index
*************** count_num_arguments (tree decl, bool *is
*** 4318,4352 ****
  static unsigned int
  create_function_info_for (tree decl, const char *name)
  {
    varinfo_t vi;
    tree arg;
    unsigned int i;
    bool is_varargs = false;
  
    /* Create the variable info.  */
  
    vi = new_var_info (decl, name);
    vi->offset = 0;
    vi->size = 1;
!   vi->fullsize = count_num_arguments (decl, &is_varargs) + 1;
    insert_vi_for_tree (vi->decl, vi);
  
    stats.total_vars++;
  
!   /* If it's varargs, we don't know how many arguments it has, so we
!      can't do much.  */
!   if (is_varargs)
      {
!       vi->fullsize = ~0;
!       vi->size = ~0;
!       vi->is_unknown_size_var = true;
!       return vi->id;
      }
  
!   arg = DECL_ARGUMENTS (decl);
  
    /* Set up variables for each argument.  */
!   for (i = 1; i < vi->fullsize; i++)
      {
        varinfo_t argvi;
        const char *newname;
--- 4764,4877 ----
  static unsigned int
  create_function_info_for (tree decl, const char *name)
  {
+   struct function *fn = DECL_STRUCT_FUNCTION (decl);
    varinfo_t vi;
    tree arg;
    unsigned int i;
    bool is_varargs = false;
+   unsigned int num_args = count_num_arguments (decl, &is_varargs);
  
    /* Create the variable info.  */
  
    vi = new_var_info (decl, name);
    vi->offset = 0;
    vi->size = 1;
!   vi->fullsize = fi_parm_base + num_args;
!   vi->is_fn_info = 1;
!   if (is_varargs)
!     vi->fullsize = ~0;
    insert_vi_for_tree (vi->decl, vi);
  
    stats.total_vars++;
  
!   /* Create a variable for things the function clobbers and one for
!      things the function uses.  */
      {
!       varinfo_t clobbervi, usevi;
!       const char *newname;
!       char *tempname;
! 
!       asprintf (&tempname, "%s.clobber", name);
!       newname = ggc_strdup (tempname);
!       free (tempname);
! 
!       clobbervi = new_var_info (NULL, newname);
!       clobbervi->offset = fi_clobbers;
!       clobbervi->size = 1;
!       clobbervi->fullsize = vi->fullsize;
!       clobbervi->is_full_var = true;
!       clobbervi->is_global_var = false;
!       insert_into_field_list_sorted (vi, clobbervi);
!       stats.total_vars++;
! 
!       asprintf (&tempname, "%s.use", name);
!       newname = ggc_strdup (tempname);
!       free (tempname);
! 
!       usevi = new_var_info (NULL, newname);
!       usevi->offset = fi_uses;
!       usevi->size = 1;
!       usevi->fullsize = vi->fullsize;
!       usevi->is_full_var = true;
!       usevi->is_global_var = false;
!       insert_into_field_list_sorted (vi, usevi);
!       stats.total_vars++;
      }
  
!   /* And one for the static chain.  */
!   if (fn->static_chain_decl != NULL_TREE)
!     {
!       varinfo_t chainvi;
!       const char *newname;
!       char *tempname;
! 
!       asprintf (&tempname, "%s.chain", name);
!       newname = ggc_strdup (tempname);
!       free (tempname);
! 
!       chainvi = new_var_info (fn->static_chain_decl, newname);
!       chainvi->offset = fi_static_chain;
!       chainvi->size = 1;
!       chainvi->fullsize = vi->fullsize;
!       chainvi->is_full_var = true;
!       chainvi->is_global_var = false;
!       insert_into_field_list_sorted (vi, chainvi);
!       stats.total_vars++;
!       insert_vi_for_tree (fn->static_chain_decl, chainvi);
!     }
! 
!   /* Create a variable for the return var.  */
!   if (DECL_RESULT (decl) != NULL
!       || !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl))))
!     {
!       varinfo_t resultvi;
!       const char *newname;
!       char *tempname;
!       tree resultdecl = decl;
! 
!       if (DECL_RESULT (decl))
! 	resultdecl = DECL_RESULT (decl);
! 
!       asprintf (&tempname, "%s.result", name);
!       newname = ggc_strdup (tempname);
!       free (tempname);
! 
!       resultvi = new_var_info (resultdecl, newname);
!       resultvi->offset = fi_result;
!       resultvi->size = 1;
!       resultvi->fullsize = vi->fullsize;
!       resultvi->is_full_var = true;
!       if (DECL_RESULT (decl))
! 	resultvi->may_have_pointers = could_have_pointers (DECL_RESULT (decl));
!       insert_into_field_list_sorted (vi, resultvi);
!       stats.total_vars++;
!       if (DECL_RESULT (decl))
! 	insert_vi_for_tree (DECL_RESULT (decl), resultvi);
!     }
  
    /* Set up variables for each argument.  */
!   arg = DECL_ARGUMENTS (decl);
!   for (i = 0; i < num_args; i++)
      {
        varinfo_t argvi;
        const char *newname;
*************** create_function_info_for (tree decl, con
*** 4356,4372 ****
        if (arg)
  	argdecl = arg;
  
!       asprintf (&tempname, "%s.arg%d", name, i-1);
        newname = ggc_strdup (tempname);
        free (tempname);
  
        argvi = new_var_info (argdecl, newname);
!       argvi->offset = i;
        argvi->size = 1;
        argvi->is_full_var = true;
        argvi->fullsize = vi->fullsize;
        insert_into_field_list_sorted (vi, argvi);
!       stats.total_vars ++;
        if (arg)
  	{
  	  insert_vi_for_tree (arg, argvi);
--- 4881,4899 ----
        if (arg)
  	argdecl = arg;
  
!       asprintf (&tempname, "%s.arg%d", name, i);
        newname = ggc_strdup (tempname);
        free (tempname);
  
        argvi = new_var_info (argdecl, newname);
!       argvi->offset = fi_parm_base + i;
        argvi->size = 1;
        argvi->is_full_var = true;
        argvi->fullsize = vi->fullsize;
+       if (arg)
+ 	argvi->may_have_pointers = could_have_pointers (arg);
        insert_into_field_list_sorted (vi, argvi);
!       stats.total_vars++;
        if (arg)
  	{
  	  insert_vi_for_tree (arg, argvi);
*************** create_function_info_for (tree decl, con
*** 4374,4406 ****
  	}
      }
  
!   /* Create a variable for the return var.  */
!   if (DECL_RESULT (decl) != NULL
!       || !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl))))
      {
!       varinfo_t resultvi;
        const char *newname;
        char *tempname;
!       tree resultdecl = decl;
  
!       vi->fullsize ++;
! 
!       if (DECL_RESULT (decl))
! 	resultdecl = DECL_RESULT (decl);
! 
!       asprintf (&tempname, "%s.result", name);
        newname = ggc_strdup (tempname);
        free (tempname);
  
!       resultvi = new_var_info (resultdecl, newname);
!       resultvi->offset = i;
!       resultvi->size = 1;
!       resultvi->fullsize = vi->fullsize;
!       resultvi->is_full_var = true;
!       insert_into_field_list_sorted (vi, resultvi);
!       stats.total_vars ++;
!       if (DECL_RESULT (decl))
! 	insert_vi_for_tree (DECL_RESULT (decl), resultvi);
      }
  
    return vi->id;
--- 4901,4930 ----
  	}
      }
  
!   /* Add one representative for all further args.  */
!   if (is_varargs)
      {
!       varinfo_t argvi;
        const char *newname;
        char *tempname;
!       tree decl;
  
!       asprintf (&tempname, "%s.varargs", name);
        newname = ggc_strdup (tempname);
        free (tempname);
  
!       /* We need sth that can be pointed to for va_start.  */
!       decl = create_tmp_var_raw (ptr_type_node, name);
!       get_var_ann (decl);
! 
!       argvi = new_var_info (decl, newname);
!       argvi->offset = fi_parm_base + num_args;
!       argvi->size = ~0;
!       argvi->is_full_var = true;
!       argvi->is_heap_var = true;
!       argvi->fullsize = vi->fullsize;
!       insert_into_field_list_sorted (vi, argvi);
!       stats.total_vars++;
      }
  
    return vi->id;
*************** set_uids_in_ptset (bitmap into, bitmap f
*** 4775,4781 ****
  	{
  	  /* Add the decl to the points-to set.  Note that the points-to
  	     set contains global variables.  */
! 	  bitmap_set_bit (into, DECL_UID (vi->decl));
  	  if (vi->is_global_var)
  	    pt->vars_contains_global = true;
  	}
--- 5299,5305 ----
  	{
  	  /* Add the decl to the points-to set.  Note that the points-to
  	     set contains global variables.  */
! 	  bitmap_set_bit (into, DECL_PT_UID (vi->decl));
  	  if (vi->is_global_var)
  	    pt->vars_contains_global = true;
  	}
*************** find_what_var_points_to (varinfo_t vi, s
*** 4810,4816 ****
  	  if (vi->id == nothing_id)
  	    pt->null = 1;
  	  else if (vi->id == escaped_id)
! 	    pt->escaped = 1;
  	  else if (vi->id == callused_id)
  	    gcc_unreachable ();
  	  else if (vi->id == nonlocal_id)
--- 5334,5345 ----
  	  if (vi->id == nothing_id)
  	    pt->null = 1;
  	  else if (vi->id == escaped_id)
! 	    {
! 	      if (in_ipa_mode)
! 		pt->ipa_escaped = 1;
! 	      else
! 		pt->escaped = 1;
! 	    }
  	  else if (vi->id == callused_id)
  	    gcc_unreachable ();
  	  else if (vi->id == nonlocal_id)
*************** find_what_p_points_to (tree p)
*** 4875,4881 ****
      return;
  
    pi = get_ptr_info (p);
!   find_what_var_points_to (vi, &pi->pt);
  }
  
  
--- 5404,5410 ----
      return;
  
    pi = get_ptr_info (p);
!   find_what_var_points_to (vi, in_ipa_mode ? &pi->ipa_pt : &pi->pt);
  }
  
  
*************** pt_solution_reset (struct pt_solution *p
*** 4918,4942 ****
  }
  
  /* Set the points-to solution *PT to point only to the variables
!    in VARS.  */
  
  void
! pt_solution_set (struct pt_solution *pt, bitmap vars)
  {
-   bitmap_iterator bi;
-   unsigned i;
- 
    memset (pt, 0, sizeof (struct pt_solution));
    pt->vars = vars;
!   EXECUTE_IF_SET_IN_BITMAP (vars, 0, i, bi)
!     {
!       tree var = referenced_var_lookup (i);
!       if (is_global_var (var))
! 	{
! 	  pt->vars_contains_global = true;
! 	  break;
! 	}
!     }
  }
  
  /* Return true if the points-to solution *PT is empty.  */
--- 5447,5464 ----
  }
  
  /* Set the points-to solution *PT to point only to the variables
!    in VARS.  VARS_CONTAINS_GLOBAL specifies whether that contains
!    global variables and VARS_CONTAINS_RESTRICT specifies whether
!    it contains restrict tag variables.  */
  
  void
! pt_solution_set (struct pt_solution *pt, bitmap vars,
! 		 bool vars_contains_global, bool vars_contains_restrict)
  {
    memset (pt, 0, sizeof (struct pt_solution));
    pt->vars = vars;
!   pt->vars_contains_global = vars_contains_global;
!   pt->vars_contains_restrict = vars_contains_restrict;
  }
  
  /* Return true if the points-to solution *PT is empty.  */
*************** pt_solution_empty_p (struct pt_solution
*** 4957,4962 ****
--- 5479,5489 ----
        && !pt_solution_empty_p (&cfun->gimple_df->escaped))
      return false;
  
+   /* If the solution includes ESCAPED, check if that is empty.  */
+   if (pt->ipa_escaped
+       && !pt_solution_empty_p (&ipa_escaped_pt))
+     return false;
+ 
    return true;
  }
  
*************** pt_solution_includes_global (struct pt_s
*** 4973,4978 ****
--- 5500,5508 ----
    if (pt->escaped)
      return pt_solution_includes_global (&cfun->gimple_df->escaped);
  
+   if (pt->ipa_escaped)
+     return pt_solution_includes_global (&ipa_escaped_pt);
+ 
    return false;
  }
  
*************** pt_solution_includes_1 (struct pt_soluti
*** 4990,4996 ****
      return true;
  
    if (pt->vars
!       && bitmap_bit_p (pt->vars, DECL_UID (decl)))
      return true;
  
    /* If the solution includes ESCAPED, check it.  */
--- 5520,5526 ----
      return true;
  
    if (pt->vars
!       && bitmap_bit_p (pt->vars, DECL_PT_UID (decl)))
      return true;
  
    /* If the solution includes ESCAPED, check it.  */
*************** pt_solution_includes_1 (struct pt_soluti
*** 4998,5003 ****
--- 5528,5538 ----
        && pt_solution_includes_1 (&cfun->gimple_df->escaped, decl))
      return true;
  
+   /* If the solution includes ESCAPED, check it.  */
+   if (pt->ipa_escaped
+       && pt_solution_includes_1 (&ipa_escaped_pt, decl))
+     return true;
+ 
    return false;
  }
  
*************** pt_solutions_intersect_1 (struct pt_solu
*** 5048,5053 ****
--- 5583,5607 ----
  	return true;
      }
  
+   /* Check the escaped solution if required.
+      ???  Do we need to check the local against the IPA escaped sets?  */
+   if ((pt1->ipa_escaped || pt2->ipa_escaped)
+       && !pt_solution_empty_p (&ipa_escaped_pt))
+     {
+       /* If both point to escaped memory and that solution
+ 	 is not empty they alias.  */
+       if (pt1->ipa_escaped && pt2->ipa_escaped)
+ 	return true;
+ 
+       /* If either points to escaped memory see if the escaped solution
+ 	 intersects with the other.  */
+       if ((pt1->ipa_escaped
+ 	   && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2))
+ 	  || (pt2->ipa_escaped
+ 	      && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1)))
+ 	return true;
+     }
+ 
    /* Now both pointers alias if their points-to solution intersects.  */
    return (pt1->vars
  	  && pt2->vars
*************** gate_ipa_pta (void)
*** 5669,5674 ****
--- 6223,6235 ----
  	  && !(errorcount || sorrycount));
  }
  
+ /* IPA PTA solutions for ESCAPED.
+    ???  We shouldn't need to compute CALLUSED or even generate constraints
+    for it.  OTOH CALLUSED represents ESCAPED but not possibly written-to
+    variables.  */
+ struct pt_solution ipa_escaped_pt
+   = { true, false, false, false, false, false, false, NULL };
+ 
  /* Execute the driver for IPA PTA.  */
  static unsigned int
  ipa_pta_execute (void)
*************** ipa_pta_execute (void)
*** 5692,5703 ****
  	  || node->clone_of)
  	continue;
  
-       /* It does not make sense to have graph edges into or out of
-          externally visible functions.  There is no extra information
- 	 we can gather from them.  */
-       if (node->local.externally_visible)
- 	continue;
- 
        varid = create_function_info_for (node->decl,
  					cgraph_node_name (node));
      }
--- 6253,6258 ----
*************** ipa_pta_execute (void)
*** 5748,5753 ****
--- 6303,6309 ----
  	      gimple stmt = gsi_stmt (gsi);
  
  	      find_func_aliases (stmt);
+ 	      find_func_clobbers (stmt);
  	    }
  	}
  
*************** ipa_pta_execute (void)
*** 5758,5763 ****
--- 6314,6366 ----
    /* From the constraints compute the points-to sets.  */
    solve_constraints ();
  
+   /* Compute the global points-to sets for ESCAPED and CALLUSED.
+      ???  These cannot be used for call-clobber analysis.  Either
+      we do not need them at all or we need them to properly build
+      call-clobber information.
+      ???  Note that the computed escape set is also not correct
+      for the whole unit as we fail to consider graph edges to
+      externally visible functions.  */
+   find_what_var_points_to (get_varinfo (escaped_id), &ipa_escaped_pt);
+ 
+   /* Make sure the ESCAPED solution (which is used as placeholder in
+      other solutions) does not reference itself.  This simplifies
+      points-to solution queries.  */
+   ipa_escaped_pt.ipa_escaped = 0;
+ 
+   /* Assign the points-to sets to the SSA names in the unit.  */
+   for (node = cgraph_nodes; node; node = node->next)
+     {
+       tree ptr;
+       struct function *fn;
+       unsigned i;
+       varinfo_t fi;
+ 
+       /* Nodes without a body are not interesting.  */
+       if (!gimple_has_body_p (node->decl)
+ 	  || node->clone_of)
+ 	continue;
+ 
+       fn = DECL_STRUCT_FUNCTION (node->decl);
+ 
+       /* Compute the points-to sets for pointer SSA_NAMEs.  */
+       for (i = 0; VEC_iterate (tree, fn->gimple_df->ssa_names, i, ptr); ++i)
+ 	{
+ 	  if (ptr
+ 	      && POINTER_TYPE_P (TREE_TYPE (ptr)))
+ 	    find_what_p_points_to (ptr);
+ 	}
+ 
+       /* Compute the clobber/use solutions.  */
+       if ((fi = lookup_vi_for_tree (node->decl)))
+ 	{
+ 	  find_what_var_points_to (first_vi_for_offset (fi, fi_clobbers),
+ 				   &fn->gimple_df->ipa_clobbered);
+ 	  find_what_var_points_to (first_vi_for_offset (fi, fi_uses),
+ 				   &fn->gimple_df->ipa_used);
+ 	}
+     }
+ 
    delete_points_to_sets ();
  
    in_ipa_mode = 0;
Index: trunk/gcc/tree-flow.h
===================================================================
*** trunk.orig/gcc/tree-flow.h	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree-flow.h	2009-11-06 14:12:13.000000000 +0100
*************** struct GTY(()) gimple_df {
*** 63,68 ****
--- 63,74 ----
    /* The PTA solution for the CALLUSED artificial variable.  */
    struct pt_solution callused;
  
+   /* The IPA PTA solutions for what is clobbered by this function.  */
+   struct pt_solution ipa_clobbered;
+ 
+   /* The IPA PTA solutions for what is clobbered by this function.  */
+   struct pt_solution ipa_used;
+ 
    /* A map of decls to artificial ssa-names that point to the partition
       of the decl.  */
    struct pointer_map_t * GTY((skip(""))) decls_to_pointers;
*************** typedef struct
*** 120,127 ****
  /* Aliasing information for SSA_NAMEs representing pointer variables.  */
  struct GTY(()) ptr_info_def
  {
!   /* The points-to solution, TBAA-pruned if the pointer is dereferenced.  */
    struct pt_solution pt;
  };
  
  
--- 126,136 ----
  /* Aliasing information for SSA_NAMEs representing pointer variables.  */
  struct GTY(()) ptr_info_def
  {
!   /* The points-to solution.  */
    struct pt_solution pt;
+ 
+   /* The IPA points-to solution.  */
+   struct pt_solution ipa_pt;
  };
  
  
Index: trunk/gcc/tree-ssa-alias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-alias.c	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree-ssa-alias.c	2009-11-06 14:12:13.000000000 +0100
*************** ptr_deref_may_alias_global_p (tree ptr)
*** 156,162 ****
      return true;
  
    /* ???  This does not use TBAA to prune globals ptr may not access.  */
!   return pt_solution_includes_global (&pi->pt);
  }
  
  /* Return true if dereferencing PTR may alias DECL.
--- 156,164 ----
      return true;
  
    /* ???  This does not use TBAA to prune globals ptr may not access.  */
!   return (pt_solution_includes_global (&pi->pt)
! 	  /* ???  This breaks things because of the call stmt oracles.  */
! 	  && (1 || !flag_ipa_pta || pt_solution_includes_global (&pi->ipa_pt)));
  }
  
  /* Return true if dereferencing PTR may alias DECL.
*************** ptr_deref_may_alias_decl_p (tree ptr, tr
*** 210,222 ****
  
    /* If the decl can be used as a restrict tag and we have a restrict
       pointer and that pointers points-to set doesn't contain this decl
!      then they can't alias.  */
    if (DECL_RESTRICTED_P (decl)
        && TYPE_RESTRICT (TREE_TYPE (ptr))
        && pi->pt.vars_contains_restrict)
!     return bitmap_bit_p (pi->pt.vars, DECL_UID (decl));
  
!   return pt_solution_includes (&pi->pt, decl);
  }
  
  /* Return true if dereferenced PTR1 and PTR2 may alias.
--- 212,226 ----
  
    /* If the decl can be used as a restrict tag and we have a restrict
       pointer and that pointers points-to set doesn't contain this decl
!      then they can't alias.
!      ???  Query IPA PTA solution as well.  */
    if (DECL_RESTRICTED_P (decl)
        && TYPE_RESTRICT (TREE_TYPE (ptr))
        && pi->pt.vars_contains_restrict)
!     return bitmap_bit_p (pi->pt.vars, DECL_PT_UID (decl));
  
!   return (pt_solution_includes (&pi->pt, decl)
! 	  && (!flag_ipa_pta || pt_solution_includes (&pi->ipa_pt, decl)));
  }
  
  /* Return true if dereferenced PTR1 and PTR2 may alias.
*************** ptr_derefs_may_alias_p (tree ptr1, tree
*** 282,288 ****
      return true;
  
    /* If both pointers are restrict-qualified try to disambiguate
!      with restrict information.  */
    if (TYPE_RESTRICT (TREE_TYPE (ptr1))
        && TYPE_RESTRICT (TREE_TYPE (ptr2))
        && !pt_solutions_same_restrict_base (&pi1->pt, &pi2->pt))
--- 286,293 ----
      return true;
  
    /* If both pointers are restrict-qualified try to disambiguate
!      with restrict information.
!      ???  Query IPA PTA solution as well.  */
    if (TYPE_RESTRICT (TREE_TYPE (ptr1))
        && TYPE_RESTRICT (TREE_TYPE (ptr2))
        && !pt_solutions_same_restrict_base (&pi1->pt, &pi2->pt))
*************** ptr_derefs_may_alias_p (tree ptr1, tree
*** 290,296 ****
  
    /* ???  This does not use TBAA to prune decls from the intersection
       that not both pointers may access.  */
!   return pt_solutions_intersect (&pi1->pt, &pi2->pt);
  }
  
  /* Return true if dereferencing PTR may alias *REF.
--- 295,303 ----
  
    /* ???  This does not use TBAA to prune decls from the intersection
       that not both pointers may access.  */
!   return (pt_solutions_intersect (&pi1->pt, &pi2->pt)
! 	  && (!flag_ipa_pta || pt_solutions_intersect (&pi1->ipa_pt,
! 						       &pi2->ipa_pt)));
  }
  
  /* Return true if dereferencing PTR may alias *REF.
*************** dump_alias_info (FILE *file)
*** 339,344 ****
--- 346,359 ----
    fprintf (file, "\nCALLUSED");
    dump_points_to_solution (file, &cfun->gimple_df->callused);
  
+   if (flag_ipa_pta)
+     {
+       fprintf (file, "\nIPA CLOBBERS");
+       dump_points_to_solution (file, &cfun->gimple_df->ipa_clobbered);
+       fprintf (file, "\nIPA USES");
+       dump_points_to_solution (file, &cfun->gimple_df->ipa_used);
+     }
+ 
    fprintf (file, "\n\nFlow-insensitive points-to information\n\n");
  
    for (i = 1; i < num_ssa_names; i++)
*************** get_ptr_info (tree t)
*** 383,388 ****
--- 398,404 ----
      {
        pi = GGC_CNEW (struct ptr_info_def);
        pt_solution_reset (&pi->pt);
+       pt_solution_reset (&pi->ipa_pt);
        SSA_NAME_PTR_INFO (t) = pi;
      }
  
*************** dump_points_to_info_for (FILE *file, tre
*** 429,434 ****
--- 445,456 ----
    else
      fprintf (file, ", points-to anything");
  
+   if (flag_ipa_pta)
+     {
+       fprintf (file, "\n\tIPA:");
+       dump_points_to_solution (file, &pi->ipa_pt);
+     }
+ 
    fprintf (file, "\n");
  }
  
*************** ref_maybe_used_by_call_p_1 (gimple call,
*** 1006,1011 ****
--- 1028,1057 ----
  	goto process_args;
      }
  
+   /* Check the IPA solution.  */
+   if (flag_ipa_pta
+       && callee != NULL_TREE
+       && DECL_STRUCT_FUNCTION (callee) != NULL
+       && DECL_STRUCT_FUNCTION (callee)->gimple_df != NULL)
+     {
+       struct function *fn = DECL_STRUCT_FUNCTION (callee);
+       if (DECL_P (base))
+ 	{
+ 	  if (!pt_solution_includes (&fn->gimple_df->ipa_used, base))
+ 	    return false;
+ 	}
+       else if (INDIRECT_REF_P (base)
+ 	       && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
+ 	{
+ 	  struct ptr_info_def *pi = SSA_NAME_PTR_INFO (TREE_OPERAND (base, 0));
+ 	  if (pi
+ 	      && !pt_solutions_intersect (&fn->gimple_df->ipa_used,
+ 					  &pi->ipa_pt))
+ 	    return false;
+ 	}
+       /* Fallthru.  */
+     }
+ 
    /* If the base variable is call-used or call-clobbered then
       it may be used.  */
    if (flags & (ECF_PURE|ECF_CONST|ECF_LOOPING_CONST_OR_PURE|ECF_NOVOPS))
*************** call_may_clobber_ref_p_1 (gimple call, a
*** 1261,1266 ****
--- 1307,1336 ----
  	return false;
      }
  
+   /* Check the IPA solution.  */
+   if (flag_ipa_pta
+       && callee != NULL_TREE
+       && DECL_STRUCT_FUNCTION (callee) != NULL
+       && DECL_STRUCT_FUNCTION (callee)->gimple_df != NULL)
+     {
+       struct function *fn = DECL_STRUCT_FUNCTION (callee);
+       if (DECL_P (base))
+ 	{
+ 	  if (!pt_solution_includes (&fn->gimple_df->ipa_clobbered, base))
+ 	    return false;
+ 	}
+       else if (INDIRECT_REF_P (base)
+ 	       && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
+ 	{
+ 	  struct ptr_info_def *pi = SSA_NAME_PTR_INFO (TREE_OPERAND (base, 0));
+ 	  if (pi
+ 	      && !pt_solutions_intersect (&fn->gimple_df->ipa_clobbered,
+ 					  &pi->ipa_pt))
+ 	    return false;
+ 	}
+       /* Fallthru.  */
+     }
+ 
    if (DECL_P (base))
      return is_call_clobbered (base);
    else if (INDIRECT_REF_P (base)
Index: trunk/gcc/tree-ssa.c
===================================================================
*** trunk.orig/gcc/tree-ssa.c	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree-ssa.c	2009-11-06 14:12:13.000000000 +0100
*************** init_tree_ssa (struct function *fn)
*** 1114,1119 ****
--- 1114,1121 ----
  				                 uid_ssaname_map_eq, NULL);
    pt_solution_reset (&fn->gimple_df->escaped);
    pt_solution_reset (&fn->gimple_df->callused);
+   pt_solution_reset (&fn->gimple_df->ipa_clobbered);
+   pt_solution_reset (&fn->gimple_df->ipa_used);
    init_ssanames (fn, 0);
    init_phinodes ();
  }
Index: trunk/gcc/cgraphunit.c
===================================================================
*** trunk.orig/gcc/cgraphunit.c	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/cgraphunit.c	2009-11-06 14:12:13.000000000 +0100
*************** cgraph_expand_function (struct cgraph_no
*** 1163,1169 ****
--- 1163,1174 ----
    gcc_assert (TREE_ASM_WRITTEN (decl));
    current_function_decl = NULL;
    gcc_assert (!cgraph_preserve_function_body_p (decl));
+ #if 0
+   /* ???  We need to put the IPA clobbered / used solutions somewhere
+      else, otherwise they get freed here before all callers have been
+      processed.  */
    cgraph_release_function_body (node);
+ #endif
    /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
       points to the dead function body.  */
    cgraph_node_remove_callees (node);
Index: trunk/gcc/final.c
===================================================================
*** trunk.orig/gcc/final.c	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/final.c	2009-11-06 14:12:13.000000000 +0100
*************** rest_of_clean_state (void)
*** 4448,4454 ****
--- 4448,4459 ----
  
    free_bb_for_insn ();
  
+ #if 0
+   /* ???  We need to put the IPA clobbered / used sets somewhere else
+      otherwise they get deleted here before all callers have been
+      processed.  */
    delete_tree_ssa ();
+ #endif
  
    if (targetm.binds_local_p (current_function_decl))
      {
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-7.c	2009-11-06 14:12:13.000000000 +0100
***************
*** 0 ****
--- 1,30 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fno-early-inlining -fipa-pta" } */
+ 
+ static void __attribute__((noinline,noclone))
+ clobber_me (int *p, int how)
+ {
+   *p = how;
+ }
+ 
+ /* When foo is inlined into main we have to make sure to adjust
+    main()s IPA CLOBBERED set according to the decl remappings
+    inlining does.  */
+ 
+ static int
+ foo (void)
+ {
+   int a = 0;
+   clobber_me (&a, 1);
+   return a;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   if (foo () != 1)
+     abort ();
+ 
+   return 0;
+ }
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-8.c	2009-11-06 14:12:13.000000000 +0100
***************
*** 0 ****
--- 1,31 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fno-early-inlining -fipa-pta" } */
+ 
+ static int *__attribute__((noinline,noclone))
+ pass_me (int *p)
+ {
+   return p;
+ }
+ 
+ /* When foo is inlined into main we have to make sure to adjust
+    main()s IPA CLOBBERED set according to the decl remappings
+    inlining does.  */
+ 
+ static int
+ foo (void)
+ {
+   int a = 0;
+   int *p = pass_me (&a);
+   *p = 1;
+   return a;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   if (foo () != 1)
+     abort ();
+ 
+   return 0;
+ }
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-9.c	2009-11-06 14:12:13.000000000 +0100
***************
*** 0 ****
--- 1,17 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta" } */
+ 
+ static void __attribute__((noinline,noclone))
+ foo (int *p, int *q)
+ {
+   __builtin_memcpy (p, q, sizeof (int));
+ }
+ extern void abort (void);
+ int main()
+ {
+   int i = 0, j = 1;
+   foo (&i, &j);
+   if (i != 1)
+     abort ();
+   return 0;
+ }
Index: trunk/gcc/tree-dfa.c
===================================================================
*** trunk.orig/gcc/tree-dfa.c	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree-dfa.c	2009-11-06 14:12:13.000000000 +0100
*************** dump_variable (FILE *file, tree var)
*** 287,292 ****
--- 287,294 ----
    ann = var_ann (var);
  
    fprintf (file, ", UID D.%u", (unsigned) DECL_UID (var));
+   if (DECL_PT_UID (var) != DECL_UID (var))
+     fprintf (file, ", PT-UID D.%u", (unsigned) DECL_PT_UID (var));
  
    fprintf (file, ", ");
    print_generic_expr (file, TREE_TYPE (var), dump_flags);
Index: trunk/gcc/tree-inline.c
===================================================================
*** trunk.orig/gcc/tree-inline.c	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree-inline.c	2009-11-06 14:12:13.000000000 +0100
*************** remap_ssa_name (tree name, copy_body_dat
*** 199,209 ****
--- 199,218 ----
        && (TREE_CODE (SSA_NAME_VAR (name)) != RESULT_DECL
  	  || !id->transform_return_to_modify))
      {
+       struct ptr_info_def *pi;
        new_tree = make_ssa_name (new_tree, NULL);
        insert_decl_map (id, name, new_tree);
        SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_tree)
  	= SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name);
        TREE_TYPE (new_tree) = TREE_TYPE (SSA_NAME_VAR (new_tree));
+       /* At least IPA points-to info can be directly transferred.  */
+       if (flag_ipa_pta
+ 	  && (pi = SSA_NAME_PTR_INFO (name))
+ 	  && !pi->ipa_pt.anything)
+ 	{
+ 	  struct ptr_info_def *new_pi = get_ptr_info (new_tree);
+ 	  new_pi->ipa_pt = pi->ipa_pt;
+ 	}
        if (gimple_nop_p (SSA_NAME_DEF_STMT (name)))
  	{
  	  /* By inlining function having uninitialized variable, we might
*************** copy_decl_to_var (tree decl, copy_body_d
*** 4533,4538 ****
--- 4542,4548 ----
  
    copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
  		     VAR_DECL, DECL_NAME (decl), type);
+   DECL_PT_UID (copy) = DECL_PT_UID (decl);
    TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
    TREE_READONLY (copy) = TREE_READONLY (decl);
    TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
*************** copy_result_decl_to_var (tree decl, copy
*** 4558,4563 ****
--- 4568,4574 ----
  
    copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
  		     VAR_DECL, DECL_NAME (decl), type);
+   DECL_PT_UID (copy) = DECL_PT_UID (decl);
    TREE_READONLY (copy) = TREE_READONLY (decl);
    TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
    if (!DECL_BY_REFERENCE (decl))
Index: trunk/gcc/tree.c
===================================================================
*** trunk.orig/gcc/tree.c	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree.c	2009-11-06 14:12:13.000000000 +0100
*************** make_node_stat (enum tree_code code MEM_
*** 883,889 ****
        if (TREE_CODE (t) == DEBUG_EXPR_DECL)
  	DECL_UID (t) = --next_debug_decl_uid;
        else
! 	DECL_UID (t) = next_decl_uid++;
        if (TREE_CODE (t) == LABEL_DECL)
  	LABEL_DECL_UID (t) = -1;
  
--- 883,892 ----
        if (TREE_CODE (t) == DEBUG_EXPR_DECL)
  	DECL_UID (t) = --next_debug_decl_uid;
        else
! 	{
! 	  DECL_UID (t) = next_decl_uid++;
! 	  DECL_PT_UID (t) = DECL_UID (t);
! 	}
        if (TREE_CODE (t) == LABEL_DECL)
  	LABEL_DECL_UID (t) = -1;
  
*************** copy_node_stat (tree node MEM_STAT_DECL)
*** 962,968 ****
        if (code == DEBUG_EXPR_DECL)
  	DECL_UID (t) = --next_debug_decl_uid;
        else
! 	DECL_UID (t) = next_decl_uid++;
        if ((TREE_CODE (node) == PARM_DECL || TREE_CODE (node) == VAR_DECL)
  	  && DECL_HAS_VALUE_EXPR_P (node))
  	{
--- 965,974 ----
        if (code == DEBUG_EXPR_DECL)
  	DECL_UID (t) = --next_debug_decl_uid;
        else
! 	{
! 	  DECL_UID (t) = next_decl_uid++;
! 	  DECL_PT_UID (t) = DECL_PT_UID (node);
! 	}
        if ((TREE_CODE (node) == PARM_DECL || TREE_CODE (node) == VAR_DECL)
  	  && DECL_HAS_VALUE_EXPR_P (node))
  	{
Index: trunk/gcc/tree.h
===================================================================
*** trunk.orig/gcc/tree.h	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree.h	2009-11-06 14:12:13.000000000 +0100
*************** struct function;
*** 2486,2491 ****
--- 2486,2495 ----
     uses.  */
  #define DEBUG_TEMP_UID(NODE) (-DECL_UID (TREE_CHECK ((NODE), DEBUG_EXPR_DECL)))
  
+ /* Every ..._DECL node gets a unique number that stays the same even
+    when the decl is copied by the inliner.  */
+ #define DECL_PT_UID(NODE) (DECL_MINIMAL_CHECK (NODE)->decl_minimal.pt_uid)
+ 
  /* These two fields describe where in the source code the declaration
     was.  If the declaration appears in several places (as for a C
     function that is declared first and then defined later), this
*************** struct GTY(()) tree_decl_minimal {
*** 2509,2514 ****
--- 2513,2519 ----
    struct tree_common common;
    location_t locus;
    unsigned int uid;
+   unsigned int pt_uid;
    tree name;
    tree context;
  };
Index: trunk/gcc/tree-ssa-alias.h
===================================================================
*** trunk.orig/gcc/tree-ssa-alias.h	2009-11-06 14:07:30.000000000 +0100
--- trunk/gcc/tree-ssa-alias.h	2009-11-06 14:20:09.000000000 +0100
*************** struct GTY(()) pt_solution
*** 38,46 ****
       even if this is zero pt_vars can still include global variables.  */
    unsigned int nonlocal : 1;
  
!   /* Nonzero if the points-to set includes any escaped local variable.  */
    unsigned int escaped : 1;
  
    /* Nonzero if the points-to set includes 'nothing', the points-to set
       includes memory at address NULL.  */
    unsigned int null : 1;
--- 38,51 ----
       even if this is zero pt_vars can still include global variables.  */
    unsigned int nonlocal : 1;
  
!   /* Nonzero if the points-to set includes the local escaped solution by
!      reference.  */
    unsigned int escaped : 1;
  
+   /* Nonzero if the points-to set includes the IPA escaped solution by
+      reference.  */
+   unsigned int ipa_escaped : 1;
+ 
    /* Nonzero if the points-to set includes 'nothing', the points-to set
       includes memory at address NULL.  */
    unsigned int null : 1;
*************** extern bool pt_solutions_intersect (stru
*** 124,131 ****
  extern bool pt_solutions_same_restrict_base (struct pt_solution *,
  					     struct pt_solution *);
  extern void pt_solution_reset (struct pt_solution *);
! extern void pt_solution_set (struct pt_solution *, bitmap);
  extern void dump_pta_stats (FILE *);
  
  
  #endif /* TREE_SSA_ALIAS_H  */
--- 129,138 ----
  extern bool pt_solutions_same_restrict_base (struct pt_solution *,
  					     struct pt_solution *);
  extern void pt_solution_reset (struct pt_solution *);
! extern void pt_solution_set (struct pt_solution *, bitmap, bool, bool);
  extern void dump_pta_stats (FILE *);
  
+ extern GTY(()) struct pt_solution ipa_escaped_pt;
+ 
  
  #endif /* TREE_SSA_ALIAS_H  */
Index: trunk/gcc/cfgexpand.c
===================================================================
*** trunk.orig/gcc/cfgexpand.c	2009-11-06 14:27:39.000000000 +0100
--- trunk/gcc/cfgexpand.c	2009-11-06 14:27:45.000000000 +0100
*************** update_alias_info_with_stack_vars (void)
*** 551,562 ****
        for (j = i; j != EOC; j = stack_vars[j].next)
  	{
  	  tree decl = stack_vars[j].decl;
! 	  unsigned int uid = DECL_UID (decl);
  	  /* We should never end up partitioning SSA names (though they
  	     may end up on the stack).  Neither should we allocate stack
  	     space to something that is unused and thus unreferenced.  */
  	  gcc_assert (DECL_P (decl)
! 		      && referenced_var_lookup (uid));
  	  bitmap_set_bit (part, uid);
  	  *((bitmap *) pointer_map_insert (decls_to_partitions,
  					   (void *)(size_t) uid)) = part;
--- 551,562 ----
        for (j = i; j != EOC; j = stack_vars[j].next)
  	{
  	  tree decl = stack_vars[j].decl;
! 	  unsigned int uid = DECL_PT_UID (decl);
  	  /* We should never end up partitioning SSA names (though they
  	     may end up on the stack).  Neither should we allocate stack
  	     space to something that is unused and thus unreferenced.  */
  	  gcc_assert (DECL_P (decl)
! 		      && referenced_var_lookup (DECL_UID (decl)));
  	  bitmap_set_bit (part, uid);
  	  *((bitmap *) pointer_map_insert (decls_to_partitions,
  					   (void *)(size_t) uid)) = part;
*************** update_alias_info_with_stack_vars (void)
*** 566,572 ****
  
        /* Make the SSA name point to all partition members.  */
        pi = get_ptr_info (name);
!       pt_solution_set (&pi->pt, part);
      }
  
    /* Make all points-to sets that contain one member of a partition
--- 566,572 ----
  
        /* Make the SSA name point to all partition members.  */
        pi = get_ptr_info (name);
!       pt_solution_set (&pi->pt, part, false, false);
      }
  
    /* Make all points-to sets that contain one member of a partition
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-10.c	2009-11-06 16:30:25.000000000 +0100
***************
*** 0 ****
--- 1,30 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ #include <stdarg.h>
+ 
+ static void __attribute__((noinline,noclone))
+ foo (int i, ...)
+ {
+   va_list ap;
+   int *p;
+   va_start (ap, i);
+   p = va_arg (ap, int *);
+   *p = 1;
+   va_end (ap);
+ }
+ extern void abort (void);
+ int main()
+ {
+   int i = 0;
+   foo (0, &i);
+   if (i != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* Verify we properly handle variadic arguments and do not let escape
+    stuff through it.  */
+ 
+ /* { dg-final { scan-ipa-dump "ESCAPED = { }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */

Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-1.c	2009-11-03 16:09:55.000000000 +0100
***************
*** 0 ****
--- 1,50 ----
+ /* { dg-do run } */
+ /* { dg-options "-O -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ static int __attribute__((noinline))
+ foo (int *p, int *q)
+ {
+   *p = 2;
+   *q = 1;
+   return *p;
+ }
+ 
+ static int __attribute__((noinline))
+ bar (int *p, int *q)
+ {
+   *p = -2;
+   *q = -1;
+   return *p;
+ }
+ 
+ static int __attribute__((noinline,noclone))
+ foobar (int foo_p)
+ {
+   int a;
+   int (*fn)(int *, int *);
+   if (foo_p)
+     fn = foo;
+   else
+     fn = bar;
+   return (*fn)(&a, &a);
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   if (foobar (1) != 1)
+     abort ();
+ 
+   return 0;
+ }
+ 
+ /* IPA PTA needs to handle indirect calls properly.  Verify that
+    both bar and foo get a (and only a) in their arguments points-to sets.  */
+ 
+ /* { dg-final { scan-ipa-dump "fn_1 = { bar foo }" "pta" } } */
+ /* { dg-final { scan-ipa-dump "bar.arg0 = { a }" "pta" } } */
+ /* { dg-final { scan-ipa-dump "bar.arg1 = { a }" "pta" } } */
+ /* { dg-final { scan-ipa-dump "foo.arg0 = { a }" "pta" } } */
+ /* { dg-final { scan-ipa-dump "foo.arg1 = { a }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-2.c	2009-11-03 16:09:55.000000000 +0100
***************
*** 0 ****
--- 1,25 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ int (*fn)(int *);
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int *p)
+ {
+   return *p;
+ }
+ 
+ extern void bar (void);
+ 
+ int main()
+ {
+   fn = foo;
+   bar ();
+   return 0;
+ }
+ 
+ /* Make sure that when a local function escapes its argument points-to sets
+    are properly adjusted.  */
+ 
+ /* { dg-final { scan-ipa-dump "foo.arg0 = { ESCAPED NONLOCAL }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-3.c	2009-11-03 16:09:55.000000000 +0100
***************
*** 0 ****
--- 1,28 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre-details" } */
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int *p, int *q)
+ {
+   *p = 1;
+   *q = 0;
+   return *p;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   int a, b;
+   if (foo (&a, &b) != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* Verify we can disambiguate *p and *q in foo.  */
+ 
+ /* { dg-final { scan-ipa-dump "foo.arg0 = &a" "pta" } } */
+ /* { dg-final { scan-ipa-dump "foo.arg1 = &b" "pta" } } */
+ /* { dg-final { scan-tree-dump "Replaced \\\*p_1\\\(D\\\) with 1" "fre" } } */
+ /* { dg-final { cleanup-tree-dump "fre" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-4.c	2009-11-03 16:09:55.000000000 +0100
***************
*** 0 ****
--- 1,33 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre-details" } */
+ 
+ int a, b;
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int *p, int *q)
+ {
+   int res;
+   *p = 1;
+   *q = 0;
+   res = *p;
+   a = 1;
+   b = 1;
+   return res;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   if (foo (&a, &b) != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* Verify we can disambiguate *p and *q in foo.  */
+ 
+ /* { dg-final { scan-ipa-dump "foo.arg0 = &a" "pta" } } */
+ /* { dg-final { scan-ipa-dump "foo.arg1 = &b" "pta" } } */
+ /* { dg-final { scan-tree-dump "Replaced \\\*p_1\\\(D\\\) with 1" "fre" } } */
+ /* { dg-final { cleanup-tree-dump "fre" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-5.c	2009-11-03 16:09:55.000000000 +0100
***************
*** 0 ****
--- 1,26 ----
+ /* { dg-do run } */
+ /* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ int **x;
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int **q)
+ {
+   int a = 1;
+   **q = 0;
+   *x = &a;
+   return **q;
+ }
+ 
+ extern void abort (void);
+ int main()
+ {
+   int b;
+   int *p = &b;
+   x = &p;
+   if (foo (&p) != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* { dg-final { cleanup-ipa-dump "pta" } } */
Index: trunk/gcc/testsuite/gcc.dg/torture/ipa-pta-2.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/torture/ipa-pta-2.c	2009-11-03 16:09:55.000000000 +0100
***************
*** 0 ****
--- 1,24 ----
+ /* { dg-do run } */
+ /* { dg-options "-fipa-pta" } */
+ 
+ int **x;
+ 
+ static int __attribute__((noinline,noclone))
+ foo (int **p)
+ {
+   int a = 1;
+   **p = 0;
+   *x = &a;
+   return **p;
+ }
+ 
+ extern void abort (void);
+ int main()
+ {
+   int b;
+   int *p = &b;
+   x = &p;
+   if (foo (&p) != 1)
+     abort ();
+   return 0;
+ }
Index: trunk/gcc/tree-ssa-structalias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-structalias.c	2009-11-03 16:09:23.000000000 +0100
--- trunk/gcc/tree-ssa-structalias.c	2009-11-03 16:10:33.000000000 +0100
*************** get_constraint_for_ssa_var (tree t, VEC(
*** 2750,2756 ****
  
    /* If we are not taking the address of the constraint expr, add all
       sub-fiels of the variable as well.  */
!   if (!address_p)
      {
        for (; vi; vi = vi->next)
  	{
--- 2750,2757 ----
  
    /* If we are not taking the address of the constraint expr, add all
       sub-fiels of the variable as well.  */
!   if (!address_p
!       && !vi->is_full_var)
      {
        for (; vi; vi = vi->next)
  	{
*************** find_func_aliases (gimple origt)
*** 3852,3858 ****
  	  else
  	    {
  	      decl = gimple_call_fn (t);
! 	      fi = get_vi_for_tree (decl);
  	    }
  
  	  /* Assign all the passed arguments to the appropriate incoming
--- 3853,3867 ----
  	  else
  	    {
  	      decl = gimple_call_fn (t);
! 	      /* The function can be either an SSA name pointer or,
! 		 worse, an OBJ_TYPE_REF.  In this case we have no
! 		 clue and should be getting ANYFN (well, ANYTHING for now).  */
! 	      if (TREE_CODE (decl) == SSA_NAME)
! 		fi = get_vi_for_tree (decl);
! 	      else if (TREE_CODE (decl) == OBJ_TYPE_REF)
! 		fi = get_varinfo (anything_id);
! 	      else
! 		gcc_unreachable ();
  	    }
  
  	  /* Assign all the passed arguments to the appropriate incoming
*************** struct gimple_opt_pass pass_build_ealias
*** 5685,5691 ****
  static bool
  gate_ipa_pta (void)
  {
!   return (flag_ipa_pta
  	  /* Don't bother doing anything if the program has errors.  */
  	  && !(errorcount || sorrycount));
  }
--- 5694,5701 ----
  static bool
  gate_ipa_pta (void)
  {
!   return (optimize
! 	  && flag_ipa_pta
  	  /* Don't bother doing anything if the program has errors.  */
  	  && !(errorcount || sorrycount));
  }
Index: trunk/gcc/tree-into-ssa.c
===================================================================
*** trunk.orig/gcc/tree-into-ssa.c	2009-11-03 16:09:23.000000000 +0100
--- trunk/gcc/tree-into-ssa.c	2009-11-03 16:09:55.000000000 +0100
*************** dump_decl_set (FILE *file, bitmap set)
*** 1459,1465 ****
  
        EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
  	{
! 	  print_generic_expr (file, referenced_var (i), 0);
  	  fprintf (file, " ");
  	}
  
--- 1459,1473 ----
  
        EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
  	{
! 	  struct tree_decl_minimal in;
! 	  tree var;
! 	  in.uid = i;
! 	  var = (tree) htab_find_with_hash (gimple_referenced_vars (cfun),
! 					    &in, i);
! 	  if (var)
! 	    print_generic_expr (file, referenced_var (i), 0);
! 	  else
! 	    fprintf (file, "D.%u", i);
  	  fprintf (file, " ");
  	}
  
Index: trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- trunk/gcc/testsuite/gcc.dg/ipa/ipa-pta-6.c	2009-11-03 16:10:04.000000000 +0100
***************
*** 0 ****
--- 1,25 ----
+ /* { dg-do run } */
+ /* { dg-options "-O -fipa-pta -fdump-ipa-pta-details" } */
+ 
+ static void __attribute__((noinline,noclone))
+ foo (int *p)
+ {
+   *p = 1;
+ }
+ 
+ extern void abort (void);
+ 
+ int main()
+ {
+   int i = 0;
+   foo (&i);
+   if (i != 1)
+     abort ();
+   return 0;
+ }
+ 
+ /* Verify we correctly compute the units ESCAPED set as empty but
+    still properly account for the store via *p in foo.  */
+ 
+ /* { dg-final { scan-ipa-dump "ESCAPED = { }" "pta" } } */
+ /* { dg-final { cleanup-ipa-dump "pta" } } */

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2009-11-06 17:12 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-06 17:38 [PATCH][RFC] IPA-PTA and how/where to store the info Richard Guenther

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).