public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Track pure function call uses separately
@ 2008-06-26 18:40 Richard Guenther
  2008-06-26 20:35 ` Daniel Berlin
  2008-06-28 13:26 ` Richard Guenther
  0 siblings, 2 replies; 8+ messages in thread
From: Richard Guenther @ 2008-06-26 18:40 UTC (permalink / raw)
  To: gcc-patches; +Cc: Diego Novillo, Daniel Berlin


This adds the ability (on top of
http://gcc.gnu.org/ml/gcc-patches/2008-06/msg01432.html - PING for that)
to track the call-uses of pure function calls separately (globbed
per function though).  This fixes the regression on tree-ssa/pr24287.c
of the aforementioned patch and should in general reduce the amount
of call-clobbered variables for functions calling pure functions.

Bootstrapped on x86_64-unknown-linux-gnu, regtesting in progress.

Ok for mainline?

Thanks,
Richard.

2008-06-26  Richard Guenther  <rguenther@suse.de>

 	* tree-ssa-structalias.c (callused_id, var_callused,
 	callused_tree): Add.
 	(handle_pure_call): New function.
 	(find_func_aliases): Call it.
 	(find_what_p_points_to): Handle the call-used set.
 	(clobber_what_escaped): Likewise.
 	(compute_call_used_vars): New function.
 	(init_base_vars): Init the call-used variable.
 	* tree-flow-inline.h (gimple_call_used_vars): New function.
 	* tree-flow.h (struct gimple_df): Add call_used_vars bitmap.
 	(compute_call_used_vars): Declare.
 	* tree-ssa-alias.c (set_initial_properties): Call
 	compute_call_used_vars.
 	(reset_alias_info): Clear call-used variables.
 	(add_call_clobber_ops): Assert we are not called for const/pure
 	functions.  Remove handling of them.
 	(add_call_read_ops): Handle pure functions by adding the
 	call-used set of variables as VUSEs.
 	* tree-ssa.c (init_tree_ssa): Allocate call-used bitmap.
 	(delete_tree_ssa): Free it.
 	* tree-dfa.c (remove_referenced_var): Clear the var from the
 	call-used bitmap.

 	* gcc.dg/tree-ssa/pr24287.c: Remove XFAIL.

Index: trunk/gcc/tree-ssa-structalias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-structalias.c	2008-06-26 17:37:55.000000000 +0200
--- trunk/gcc/tree-ssa-structalias.c	2008-06-26 17:54:22.000000000 +0200
*************** get_varinfo_fc (unsigned int n)
*** 296,302 ****

   /* Static IDs for the special variables.  */
   enum { nothing_id = 0, anything_id = 1, readonly_id = 2,
!        escaped_id = 3, nonlocal_id = 4, integer_id = 5 };

   /* Variable that represents the unknown pointer.  */
   static varinfo_t var_anything;
--- 296,302 ----

   /* Static IDs for the special variables.  */
   enum { nothing_id = 0, anything_id = 1, readonly_id = 2,
!        escaped_id = 3, nonlocal_id = 4, callused_id = 5, integer_id = 6 };

   /* Variable that represents the unknown pointer.  */
   static varinfo_t var_anything;
*************** static tree escaped_tree;
*** 318,323 ****
--- 318,327 ----
   static varinfo_t var_nonlocal;
   static tree nonlocal_tree;

+ /* Variable that represents call-used memory.  */
+ static varinfo_t var_callused;
+ static tree callused_tree;
+
   /* Variable that represents integers.  This is used for when people do things
      like &0->a.b.  */
   static varinfo_t var_integer;
*************** handle_const_call (tree stmt)
*** 3691,3696 ****
--- 3695,3755 ----
     VEC_free (ce_s, heap, lhsc);
   }

+ /* For non-IPA mode, generate constraints necessary for a call to a
+    pure function in statement STMT.  */
+ 
+ static void
+ handle_pure_call (tree stmt)
+ {
+   tree call = get_call_expr_in (stmt);
+   tree arg;
+   call_expr_arg_iterator iter;
+ 
+   /* Memory reached from pointer arguments is call-used.  */
+   FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
+     if (could_have_pointers (arg))
+       make_constraint_to (callused_id, arg);
+ 
+   /* The static chain is used as well.  */
+   if (CALL_EXPR_STATIC_CHAIN (call))
+     make_constraint_to (callused_id, CALL_EXPR_STATIC_CHAIN (call));
+ 
+   /* If the call returns a pointer it may point to reachable memory
+      from the arguments.  */
+   if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+       && could_have_pointers (GIMPLE_STMT_OPERAND (stmt, 0)))
+     {
+       tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
+       VEC(ce_s, heap) *lhsc = NULL;
+       struct constraint_expr rhsc;
+       struct constraint_expr *lhsp;
+       unsigned j;
+ 
+       get_constraint_for (lhs, &lhsc);
+ 
+       /* If this is a nested function then it can return anything.  */
+       if (CALL_EXPR_STATIC_CHAIN (call))
+ 	{
+ 	  rhsc.var = anything_id;
+ 	  rhsc.offset = 0;
+ 	  rhsc.type = ADDRESSOF;
+ 	  for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ 	    process_constraint_1 (new_constraint (*lhsp, rhsc), true);
+ 	  VEC_free (ce_s, heap, lhsc);
+ 	  return;
+ 	}
+ 
+       /* Else just add the call-used memory here.  Escaped variables
+          and globals will be dealt with in handle_lhs_call.  */
+       rhsc.var = callused_id;
+       rhsc.offset = 0;
+       rhsc.type = ADDRESSOF;
+       for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ 	process_constraint_1 (new_constraint (*lhsp, rhsc), true);
+       VEC_free (ce_s, heap, lhsc);
+     }
+ }
+
   /* 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 (tree origt)
*** 3767,3772 ****
--- 3826,3838 ----
   		  && could_have_pointers (GIMPLE_STMT_OPERAND (t, 1)))
   		handle_const_call (t);
   	    }
+ 	  else if (flags & ECF_PURE)
+ 	    {
+ 	      handle_pure_call (t);
+ 	      if (TREE_CODE (t) == GIMPLE_MODIFY_STMT
+ 		  && could_have_pointers (GIMPLE_STMT_OPERAND (t, 1)))
+ 		handle_lhs_call (GIMPLE_STMT_OPERAND (t, 0));
+ 	    }
   	  /* Pure functions can return addresses in and of memory
   	     reachable from their arguments, but they are not an escape
   	     point for reachable memory of their arguments.  But as we
*************** find_what_p_points_to (tree p)
*** 4928,4934 ****
   		    pi->pt_null = 1;
   		  else if (vi->id == anything_id
   			   || vi->id == nonlocal_id
! 			   || vi->id == escaped_id)
   		    was_pt_anything = 1;
   		  else if (vi->id == readonly_id)
   		    was_pt_anything = 1;
--- 4994,5001 ----
   		    pi->pt_null = 1;
   		  else if (vi->id == anything_id
   			   || vi->id == nonlocal_id
! 			   || vi->id == escaped_id
! 			   || vi->id == callused_id)
   		    was_pt_anything = 1;
   		  else if (vi->id == readonly_id)
   		    was_pt_anything = 1;
*************** clobber_what_escaped (void)
*** 4995,5000 ****
--- 5062,5076 ----
        variable for escaped_id.  */
     vi = get_varinfo (find (escaped_id));

+   /* If call-used memory escapes we need to include it in the
+      set of escaped variables.  This can happen if a pure
+      function returns a pointer and this pointer escapes.  */
+   if (bitmap_bit_p (vi->solution, callused_id))
+     {
+       varinfo_t cu_vi = get_varinfo (find (callused_id));
+       bitmap_ior_into (vi->solution, cu_vi->solution);
+     }
+
     /* Mark variables in the solution call-clobbered.  */
     EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
       {
*************** clobber_what_escaped (void)
*** 5024,5029 ****
--- 5100,5153 ----
     return true;
   }

+ /* Compute the call-used variables.  */
+ 
+ void
+ compute_call_used_vars (void)
+ {
+   varinfo_t vi;
+   unsigned int i;
+   bitmap_iterator bi;
+   bool has_anything_id = false;
+ 
+   if (!have_alias_info)
+     return;
+ 
+   /* This variable may have been collapsed, let's get the real
+      variable for escaped_id.  */
+   vi = get_varinfo (find (callused_id));
+ 
+   /* Mark variables in the solution call-clobbered.  */
+   EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
+     {
+       varinfo_t vi = get_varinfo (i);
+ 
+       if (vi->is_artificial_var)
+ 	{
+ 	  /* For anything_id and integer_id we need to make
+ 	     all local addressable vars call-used.  */
+ 	  if (vi->id == anything_id
+ 	      || vi->id == integer_id)
+ 	    has_anything_id = true;
+ 	}
+ 
+       /* Only artificial heap-vars are further interesting.  */
+       if (vi->is_artificial_var && !vi->is_heap_var)
+ 	continue;
+ 
+       if ((TREE_CODE (vi->decl) == VAR_DECL
+ 	   || TREE_CODE (vi->decl) == PARM_DECL
+ 	   || TREE_CODE (vi->decl) == RESULT_DECL)
+ 	  && !unmodifiable_var_p (vi->decl))
+ 	bitmap_set_bit (gimple_call_used_vars (cfun), DECL_UID (vi->decl));
+     }
+ 
+   /* If anything is call-used, add all addressable locals to the set.  */
+   if (has_anything_id)
+     bitmap_ior_into (gimple_call_used_vars (cfun),
+ 		     gimple_addressable_vars (cfun));
+ }
+

   /* Dump points-to information to OUTFILE.  */

*************** init_base_vars (void)
*** 5171,5176 ****
--- 5295,5321 ----
     var_nonlocal->is_special_var = 1;
     VEC_safe_push (varinfo_t, heap, varmap, var_nonlocal);

+   /* Create the CALLUSED variable, used to represent the set of call-used
+      memory.  */
+   callused_tree = create_tmp_var_raw (void_type_node, "CALLUSED");
+   var_callused = new_var_info (callused_tree, callused_id, "CALLUSED");
+   insert_vi_for_tree (callused_tree, var_callused);
+   var_callused->is_artificial_var = 1;
+   var_callused->offset = 0;
+   var_callused->size = ~0;
+   var_callused->fullsize = ~0;
+   var_callused->is_special_var = 0;
+   VEC_safe_push (varinfo_t, heap, varmap, var_callused);
+ 
+   /* CALLUSED = *CALLUSED, because call-used is may-deref'd at calls, etc.  */
+   lhs.type = SCALAR;
+   lhs.var = callused_id;
+   lhs.offset = 0;
+   rhs.type = DEREF;
+   rhs.var = callused_id;
+   rhs.offset = 0;
+   process_constraint_1 (new_constraint (lhs, rhs), true);
+
     /* Create the INTEGER variable, used to represent that a variable points
        to an INTEGER.  */
     integer_tree = create_tmp_var_raw (void_type_node, "INTEGER");
Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c
===================================================================
*** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c	2008-06-26 17:37:37.000000000 +0200
--- trunk/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c	2008-06-26 17:38:02.000000000 +0200
*************** int g(void)
*** 21,25 ****
       link_error ();
     return t2 == 2;
   }
! /* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" { xfail *-*-* } } } */
   /* { dg-final { cleanup-tree-dump "optimized" } } */
--- 21,25 ----
       link_error ();
     return t2 == 2;
   }
! /* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" } } */
   /* { dg-final { cleanup-tree-dump "optimized" } } */
Index: trunk/gcc/tree-flow-inline.h
===================================================================
*** trunk.orig/gcc/tree-flow-inline.h	2008-06-26 17:37:37.000000000 +0200
--- trunk/gcc/tree-flow-inline.h	2008-06-26 17:38:02.000000000 +0200
*************** gimple_call_clobbered_vars (const struct
*** 66,71 ****
--- 66,80 ----
     return fun->gimple_df->call_clobbered_vars;
   }

+ /* Call-used variables in the function.  If bit I is set, then
+    REFERENCED_VARS (I) is call-used at pure function call-sites.  */
+ static inline bitmap
+ gimple_call_used_vars (const struct function *fun)
+ {
+   gcc_assert (fun && fun->gimple_df);
+   return fun->gimple_df->call_used_vars;
+ }
+
   /* Array of all variables referenced in the function.  */
   static inline htab_t
   gimple_referenced_vars (const struct function *fun)
Index: trunk/gcc/tree-flow.h
===================================================================
*** trunk.orig/gcc/tree-flow.h	2008-06-26 17:37:37.000000000 +0200
--- trunk/gcc/tree-flow.h	2008-06-26 17:38:02.000000000 +0200
*************** struct gimple_df GTY(())
*** 162,167 ****
--- 162,171 ----
        REFERENCED_VARS (I) is call-clobbered.  */
     bitmap call_clobbered_vars;

+   /* Call-used variables in the function.  If bit I is set, then
+      REFERENCED_VARS (I) is call-used at pure function call-sites.  */
+   bitmap call_used_vars;
+
     /* Addressable variables in the function.  If bit I is set, then
        REFERENCED_VARS (I) has had its address taken.  Note that
        CALL_CLOBBERED_VARS and ADDRESSABLE_VARS are not related.  An
*************** tree gimple_fold_indirect_ref (tree);
*** 1174,1179 ****
--- 1178,1184 ----
   /* In tree-ssa-structalias.c */
   bool find_what_p_points_to (tree);
   bool clobber_what_escaped (void);
+ void compute_call_used_vars (void);

   /* In tree-ssa-live.c */
   extern void remove_unused_locals (void);
Index: trunk/gcc/tree-ssa-alias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-alias.c	2008-06-26 17:37:37.000000000 +0200
--- trunk/gcc/tree-ssa-alias.c	2008-06-26 17:38:02.000000000 +0200
*************** compute_tag_properties (void)
*** 505,511 ****
     VEC_free (tree, heap, taglist);
   }

! /* Set up the initial variable clobbers and globalness.
      When this function completes, only tags whose aliases need to be
      clobbered will be set clobbered.  Tags clobbered because they
      contain call clobbered vars are handled in compute_tag_properties.  */
--- 505,511 ----
     VEC_free (tree, heap, taglist);
   }

! /* Set up the initial variable clobbers, call-uses and globalness.
      When this function completes, only tags whose aliases need to be
      clobbered will be set clobbered.  Tags clobbered because they
      contain call clobbered vars are handled in compute_tag_properties.  */
*************** set_initial_properties (struct alias_inf
*** 543,548 ****
--- 543,550 ----
         pt_anything_mask |= ESCAPE_TO_CALL;
       }

+   compute_call_used_vars ();
+
     for (i = 0; VEC_iterate (tree, ai->processed_ptrs, i, ptr); i++)
       {
         struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
*************** reset_alias_info (void)
*** 2000,2005 ****
--- 2002,2010 ----
     /* There should be no call-clobbered variable left.  */
     gcc_assert (bitmap_empty_p (gimple_call_clobbered_vars (cfun)));

+   /* Clear the call-used variables.  */
+   bitmap_clear (gimple_call_used_vars (cfun));
+
     /* Clear flow-sensitive points-to information from each SSA name.  */
     for (i = 1; i < num_ssa_names; i++)
       {
Index: trunk/gcc/tree-ssa-operands.c
===================================================================
*** trunk.orig/gcc/tree-ssa-operands.c	2008-06-26 17:37:37.000000000 +0200
--- trunk/gcc/tree-ssa-operands.c	2008-06-26 18:13:07.000000000 +0200
*************** add_call_clobber_ops (tree stmt, tree ca
*** 1660,1666 ****
     bitmap_iterator bi;
     stmt_ann_t s_ann = stmt_ann (stmt);
     bitmap not_read_b, not_written_b;
!
     /* If we created .GLOBAL_VAR earlier, just use it.  */
     if (gimple_global_var (cfun))
       {
--- 1660,1669 ----
     bitmap_iterator bi;
     stmt_ann_t s_ann = stmt_ann (stmt);
     bitmap not_read_b, not_written_b;
!   tree call = get_call_expr_in (stmt);
! 
!   gcc_assert (!(call_expr_flags (call) & (ECF_PURE | ECF_CONST)));
!
     /* If we created .GLOBAL_VAR earlier, just use it.  */
     if (gimple_global_var (cfun))
       {
*************** add_call_clobber_ops (tree stmt, tree ca
*** 1674,1685 ****
        or write that variable.  */
     not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL;
     not_written_b = callee ? ipa_reference_get_not_written_global (callee) : NULL; 
-
     /* Add a VDEF operand for every call clobbered variable.  */
     EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
       {
         tree var = referenced_var_lookup (u);
-       unsigned int escape_mask = var_ann (var)->escape_mask;
         tree real_var = var;
         bool not_read;
         bool not_written;
--- 1677,1686 ----
*************** add_call_clobber_ops (tree stmt, tree ca
*** 1697,1720 ****

         /* See if this variable is really clobbered by this function.  */

-       /* Trivial case: Things escaping only to pure/const are not
- 	 clobbered by non-pure-const, and only read by pure/const. */
-       if ((escape_mask & ~(ESCAPE_TO_PURE_CONST)) == 0)
- 	{
- 	  tree call = get_call_expr_in (stmt);
- 	  if (call_expr_flags (call) & (ECF_CONST | ECF_PURE))
- 	    {
- 	      add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
- 	      clobber_stats.unescapable_clobbers_avoided++;
- 	      continue;
- 	    }
- 	  else
- 	    {
- 	      clobber_stats.unescapable_clobbers_avoided++;
- 	      continue;
- 	    }
- 	}
-
         if (not_written)
   	{
   	  clobber_stats.static_write_clobbers_avoided++;
--- 1698,1703 ----
*************** add_call_read_ops (tree stmt, tree calle
*** 1739,1756 ****
     bitmap_iterator bi;
     stmt_ann_t s_ann = stmt_ann (stmt);
     bitmap not_read_b;

!   /* if the function is not pure, it may reference memory.  Add
!      a VUSE for .GLOBAL_VAR if it has been created.  See add_referenced_var
!      for the heuristic used to decide whether to create .GLOBAL_VAR.  */
     if (gimple_global_var (cfun))
       {
         tree var = gimple_global_var (cfun);
         add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
         return;
       }
- 
-   not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL;

     /* Add a VUSE for each call-clobbered variable.  */
     EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
--- 1722,1768 ----
     bitmap_iterator bi;
     stmt_ann_t s_ann = stmt_ann (stmt);
     bitmap not_read_b;
+   tree call = get_call_expr_in (stmt);

!   /* Const functions do not reference memory.  */
!   if (call_expr_flags (call) & ECF_CONST)
!     return;
! 
!   not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL;
! 
!   /* For pure functions we compute non-escaped uses separately.  */
!   if (call_expr_flags (call) & ECF_PURE)
!     EXECUTE_IF_SET_IN_BITMAP (gimple_call_used_vars (cfun), 0, u, bi)
!       {
! 	tree var = referenced_var_lookup (u);
! 	tree real_var = var;
! 	bool not_read;
! 
! 	if (unmodifiable_var_p (var))
! 	  continue;
! 
! 	not_read = not_read_b
! 	    ? bitmap_bit_p (not_read_b, DECL_UID (real_var))
! 	    : false;
! 
! 	clobber_stats.readonly_clobbers++;
! 
! 	/* See if this variable is really used by this function.  */
! 	if (!not_read)
! 	  add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
! 	else
! 	  clobber_stats.static_readonly_clobbers_avoided++;
!       }
! 
!   /* Add a VUSE for .GLOBAL_VAR if it has been created.  See
!      add_referenced_var for the heuristic used to decide whether to
!      create .GLOBAL_VAR.  */
     if (gimple_global_var (cfun))
       {
         tree var = gimple_global_var (cfun);
         add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
         return;
       }

     /* Add a VUSE for each call-clobbered variable.  */
     EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
Index: trunk/gcc/tree-ssa.c
===================================================================
*** trunk.orig/gcc/tree-ssa.c	2008-06-26 17:37:37.000000000 +0200
--- trunk/gcc/tree-ssa.c	2008-06-26 17:38:02.000000000 +0200
*************** init_tree_ssa (struct function *fn)
*** 937,942 ****
--- 937,943 ----
     fn->gimple_df->default_defs = htab_create_ggc (20, uid_ssaname_map_hash,
   				                 uid_ssaname_map_eq, NULL);
     fn->gimple_df->call_clobbered_vars = BITMAP_GGC_ALLOC ();
+   fn->gimple_df->call_used_vars = BITMAP_GGC_ALLOC ();
     fn->gimple_df->addressable_vars = BITMAP_GGC_ALLOC ();
     init_ssanames (fn, 0);
     init_phinodes ();
*************** delete_tree_ssa (void)
*** 1009,1014 ****
--- 1010,1016 ----
     htab_delete (cfun->gimple_df->default_defs);
     cfun->gimple_df->default_defs = NULL;
     cfun->gimple_df->call_clobbered_vars = NULL;
+   cfun->gimple_df->call_used_vars = NULL;
     cfun->gimple_df->addressable_vars = NULL;
     cfun->gimple_df->modified_noreturn_calls = NULL;
     if (gimple_aliases_computed_p (cfun))
Index: trunk/gcc/tree-dfa.c
===================================================================
*** trunk.orig/gcc/tree-dfa.c	2008-06-26 17:37:37.000000000 +0200
--- trunk/gcc/tree-dfa.c	2008-06-26 17:38:02.000000000 +0200
*************** remove_referenced_var (tree var)
*** 746,751 ****
--- 746,752 ----
     unsigned int uid = DECL_UID (var);

     clear_call_clobbered (var);
+   bitmap_clear_bit (gimple_call_used_vars (cfun), uid);
     if ((v_ann = var_ann (var)))
       {
         /* Preserve var_anns of globals, but clear their alias info.  */

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Track pure function call uses separately
  2008-06-26 18:40 [PATCH] Track pure function call uses separately Richard Guenther
@ 2008-06-26 20:35 ` Daniel Berlin
  2008-06-26 20:46   ` Richard Guenther
  2008-06-27 10:53   ` Richard Guenther
  2008-06-28 13:26 ` Richard Guenther
  1 sibling, 2 replies; 8+ messages in thread
From: Daniel Berlin @ 2008-06-26 20:35 UTC (permalink / raw)
  To: Richard Guenther; +Cc: gcc-patches, Diego Novillo

On Thu, Jun 26, 2008 at 12:34 PM, Richard Guenther <rguenther@suse.de> wrote:
>
> This adds the ability (on top of
> http://gcc.gnu.org/ml/gcc-patches/2008-06/msg01432.html - PING for that)

I thought I okayed that one on IRC.
I will do so explicitly in a moment.


> to track the call-uses of pure function calls separately (globbed
> per function though).  This fixes the regression on tree-ssa/pr24287.c
> of the aforementioned patch and should in general reduce the amount
> of call-clobbered variables for functions calling pure functions.

Does this mean i can do the pure/const patch i had before and not have
it break stuff?
:)

>
> Bootstrapped on x86_64-unknown-linux-gnu, regtesting in progress.
>
> Ok for mainline?
>
> Thanks,
> Richard.
>
> 2008-06-26  Richard Guenther  <rguenther@suse.de>
>
>        * tree-ssa-structalias.c (callused_id, var_callused,
>        callused_tree): Add.
>        (handle_pure_call): New function.
>        (find_func_aliases): Call it.
>        (find_what_p_points_to): Handle the call-used set.
>        (clobber_what_escaped): Likewise.
>        (compute_call_used_vars): New function.
>        (init_base_vars): Init the call-used variable.
>        * tree-flow-inline.h (gimple_call_used_vars): New function.
>        * tree-flow.h (struct gimple_df): Add call_used_vars bitmap.
>        (compute_call_used_vars): Declare.
>        * tree-ssa-alias.c (set_initial_properties): Call
>        compute_call_used_vars.
>        (reset_alias_info): Clear call-used variables.
>        (add_call_clobber_ops): Assert we are not called for const/pure
>        functions.  Remove handling of them.
>        (add_call_read_ops): Handle pure functions by adding the
>        call-used set of variables as VUSEs.
>        * tree-ssa.c (init_tree_ssa): Allocate call-used bitmap.
>        (delete_tree_ssa): Free it.
>        * tree-dfa.c (remove_referenced_var): Clear the var from the
>        call-used bitmap.
>
>        * gcc.dg/tree-ssa/pr24287.c: Remove XFAIL.
>
> Index: trunk/gcc/tree-ssa-structalias.c
> ===================================================================
> *** trunk.orig/gcc/tree-ssa-structalias.c       2008-06-26
> 17:37:55.000000000 +0200
> --- trunk/gcc/tree-ssa-structalias.c    2008-06-26 17:54:22.000000000 +0200
> *************** get_varinfo_fc (unsigned int n)
> *** 296,302 ****
>
>  /* Static IDs for the special variables.  */
>  enum { nothing_id = 0, anything_id = 1, readonly_id = 2,
> !        escaped_id = 3, nonlocal_id = 4, integer_id = 5 };
>
>  /* Variable that represents the unknown pointer.  */
>  static varinfo_t var_anything;
> --- 296,302 ----
>
>  /* Static IDs for the special variables.  */
>  enum { nothing_id = 0, anything_id = 1, readonly_id = 2,
> !        escaped_id = 3, nonlocal_id = 4, callused_id = 5, integer_id = 6 };
>
>  /* Variable that represents the unknown pointer.  */
>  static varinfo_t var_anything;
> *************** static tree escaped_tree;
> *** 318,323 ****
> --- 318,327 ----
>  static varinfo_t var_nonlocal;
>  static tree nonlocal_tree;
>
> + /* Variable that represents call-used memory.  */
> + static varinfo_t var_callused;
> + static tree callused_tree;
> +
>  /* Variable that represents integers.  This is used for when people do
> things
>     like &0->a.b.  */
>  static varinfo_t var_integer;
> *************** handle_const_call (tree stmt)
> *** 3691,3696 ****
> --- 3695,3755 ----
>    VEC_free (ce_s, heap, lhsc);
>  }
>
> + /* For non-IPA mode, generate constraints necessary for a call to a
> +    pure function in statement STMT.  */
> + + static void
> + handle_pure_call (tree stmt)
> + {
> +   tree call = get_call_expr_in (stmt);
> +   tree arg;
> +   call_expr_arg_iterator iter;
> + +   /* Memory reached from pointer arguments is call-used.  */
> +   FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
> +     if (could_have_pointers (arg))
> +       make_constraint_to (callused_id, arg);
> + +   /* The static chain is used as well.  */
> +   if (CALL_EXPR_STATIC_CHAIN (call))
> +     make_constraint_to (callused_id, CALL_EXPR_STATIC_CHAIN (call));
> + +   /* If the call returns a pointer it may point to reachable memory
> +      from the arguments.  */
> +   if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
> +       && could_have_pointers (GIMPLE_STMT_OPERAND (stmt, 0)))
> +     {
> +       tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
> +       VEC(ce_s, heap) *lhsc = NULL;
> +       struct constraint_expr rhsc;
> +       struct constraint_expr *lhsp;
> +       unsigned j;
> + +       get_constraint_for (lhs, &lhsc);
> + +       /* If this is a nested function then it can return anything.  */
> +       if (CALL_EXPR_STATIC_CHAIN (call))
> +       {
> +         rhsc.var = anything_id;
> +         rhsc.offset = 0;
> +         rhsc.type = ADDRESSOF;
> +         for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
> +           process_constraint_1 (new_constraint (*lhsp, rhsc), true);
> +         VEC_free (ce_s, heap, lhsc);
> +         return;
> +       }
> + +       /* Else just add the call-used memory here.  Escaped variables
> +          and globals will be dealt with in handle_lhs_call.  */
> +       rhsc.var = callused_id;
> +       rhsc.offset = 0;
> +       rhsc.type = ADDRESSOF;
> +       for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
> +       process_constraint_1 (new_constraint (*lhsp, rhsc), true);
> +       VEC_free (ce_s, heap, lhsc);
> +     }
> + }
> +
>  /* 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 (tree origt)
> *** 3767,3772 ****
> --- 3826,3838 ----
>                  && could_have_pointers (GIMPLE_STMT_OPERAND (t, 1)))
>                handle_const_call (t);
>            }
> +         else if (flags & ECF_PURE)
> +           {
> +             handle_pure_call (t);
> +             if (TREE_CODE (t) == GIMPLE_MODIFY_STMT
> +                 && could_have_pointers (GIMPLE_STMT_OPERAND (t, 1)))
> +               handle_lhs_call (GIMPLE_STMT_OPERAND (t, 0));
> +           }
>          /* Pure functions can return addresses in and of memory
>             reachable from their arguments, but they are not an escape
>             point for reachable memory of their arguments.  But as we
> *************** find_what_p_points_to (tree p)
> *** 4928,4934 ****
>                    pi->pt_null = 1;
>                  else if (vi->id == anything_id
>                           || vi->id == nonlocal_id
> !                          || vi->id == escaped_id)
>                    was_pt_anything = 1;
>                  else if (vi->id == readonly_id)
>                    was_pt_anything = 1;
> --- 4994,5001 ----
>                    pi->pt_null = 1;
>                  else if (vi->id == anything_id
>                           || vi->id == nonlocal_id
> !                          || vi->id == escaped_id
> !                          || vi->id == callused_id)
>                    was_pt_anything = 1;
>                  else if (vi->id == readonly_id)
>                    was_pt_anything = 1;
> *************** clobber_what_escaped (void)
> *** 4995,5000 ****
> --- 5062,5076 ----
>       variable for escaped_id.  */
>    vi = get_varinfo (find (escaped_id));
>
> +   /* If call-used memory escapes we need to include it in the
> +      set of escaped variables.  This can happen if a pure
> +      function returns a pointer and this pointer escapes.  */
> +   if (bitmap_bit_p (vi->solution, callused_id))
> +     {
> +       varinfo_t cu_vi = get_varinfo (find (callused_id));
> +       bitmap_ior_into (vi->solution, cu_vi->solution);
> +     }
> +
>    /* Mark variables in the solution call-clobbered.  */
>    EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
>      {
> *************** clobber_what_escaped (void)
> *** 5024,5029 ****
> --- 5100,5153 ----
>    return true;
>  }
>
> + /* Compute the call-used variables.  */
> + + void
> + compute_call_used_vars (void)
> + {
> +   varinfo_t vi;
> +   unsigned int i;
> +   bitmap_iterator bi;
> +   bool has_anything_id = false;
> + +   if (!have_alias_info)
> +     return;
> + +   /* This variable may have been collapsed, let's get the real
> +      variable for escaped_id.  */
> +   vi = get_varinfo (find (callused_id));
> + +   /* Mark variables in the solution call-clobbered.  */
> +   EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
> +     {
> +       varinfo_t vi = get_varinfo (i);
> + +       if (vi->is_artificial_var)
> +       {
> +         /* For anything_id and integer_id we need to make
> +            all local addressable vars call-used.  */
> +         if (vi->id == anything_id
> +             || vi->id == integer_id)
> +           has_anything_id = true;
> +       }
> + +       /* Only artificial heap-vars are further interesting.  */
> +       if (vi->is_artificial_var && !vi->is_heap_var)
> +       continue;
> + +       if ((TREE_CODE (vi->decl) == VAR_DECL
> +          || TREE_CODE (vi->decl) == PARM_DECL
> +          || TREE_CODE (vi->decl) == RESULT_DECL)
> +         && !unmodifiable_var_p (vi->decl))
> +       bitmap_set_bit (gimple_call_used_vars (cfun), DECL_UID (vi->decl));
> +     }
> + +   /* If anything is call-used, add all addressable locals to the set.
>  */
> +   if (has_anything_id)
> +     bitmap_ior_into (gimple_call_used_vars (cfun),
> +                    gimple_addressable_vars (cfun));
> + }
> +
>
>  /* Dump points-to information to OUTFILE.  */
>
> *************** init_base_vars (void)
> *** 5171,5176 ****
> --- 5295,5321 ----
>    var_nonlocal->is_special_var = 1;
>    VEC_safe_push (varinfo_t, heap, varmap, var_nonlocal);
>
> +   /* Create the CALLUSED variable, used to represent the set of call-used
> +      memory.  */
> +   callused_tree = create_tmp_var_raw (void_type_node, "CALLUSED");
> +   var_callused = new_var_info (callused_tree, callused_id, "CALLUSED");
> +   insert_vi_for_tree (callused_tree, var_callused);
> +   var_callused->is_artificial_var = 1;
> +   var_callused->offset = 0;
> +   var_callused->size = ~0;
> +   var_callused->fullsize = ~0;
> +   var_callused->is_special_var = 0;
> +   VEC_safe_push (varinfo_t, heap, varmap, var_callused);
> + +   /* CALLUSED = *CALLUSED, because call-used is may-deref'd at calls,
> etc.  */
> +   lhs.type = SCALAR;
> +   lhs.var = callused_id;
> +   lhs.offset = 0;
> +   rhs.type = DEREF;
> +   rhs.var = callused_id;
> +   rhs.offset = 0;
> +   process_constraint_1 (new_constraint (lhs, rhs), true);
> +
>    /* Create the INTEGER variable, used to represent that a variable points
>       to an INTEGER.  */
>    integer_tree = create_tmp_var_raw (void_type_node, "INTEGER");
> Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c
> ===================================================================
> *** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c  2008-06-26
> 17:37:37.000000000 +0200
> --- trunk/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c       2008-06-26
> 17:38:02.000000000 +0200
> *************** int g(void)
> *** 21,25 ****
>      link_error ();
>    return t2 == 2;
>  }
> ! /* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" { xfail
> *-*-* } } } */
>  /* { dg-final { cleanup-tree-dump "optimized" } } */
> --- 21,25 ----
>      link_error ();
>    return t2 == 2;
>  }
> ! /* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" } } */
>  /* { dg-final { cleanup-tree-dump "optimized" } } */
> Index: trunk/gcc/tree-flow-inline.h
> ===================================================================
> *** trunk.orig/gcc/tree-flow-inline.h   2008-06-26 17:37:37.000000000 +0200
> --- trunk/gcc/tree-flow-inline.h        2008-06-26 17:38:02.000000000 +0200
> *************** gimple_call_clobbered_vars (const struct
> *** 66,71 ****
> --- 66,80 ----
>    return fun->gimple_df->call_clobbered_vars;
>  }
>
> + /* Call-used variables in the function.  If bit I is set, then
> +    REFERENCED_VARS (I) is call-used at pure function call-sites.  */
> + static inline bitmap
> + gimple_call_used_vars (const struct function *fun)
> + {
> +   gcc_assert (fun && fun->gimple_df);
> +   return fun->gimple_df->call_used_vars;
> + }
> +
>  /* Array of all variables referenced in the function.  */
>  static inline htab_t
>  gimple_referenced_vars (const struct function *fun)
> Index: trunk/gcc/tree-flow.h
> ===================================================================
> *** trunk.orig/gcc/tree-flow.h  2008-06-26 17:37:37.000000000 +0200
> --- trunk/gcc/tree-flow.h       2008-06-26 17:38:02.000000000 +0200
> *************** struct gimple_df GTY(())
> *** 162,167 ****
> --- 162,171 ----
>       REFERENCED_VARS (I) is call-clobbered.  */
>    bitmap call_clobbered_vars;
>
> +   /* Call-used variables in the function.  If bit I is set, then
> +      REFERENCED_VARS (I) is call-used at pure function call-sites.  */
> +   bitmap call_used_vars;
> +
>    /* Addressable variables in the function.  If bit I is set, then
>       REFERENCED_VARS (I) has had its address taken.  Note that
>       CALL_CLOBBERED_VARS and ADDRESSABLE_VARS are not related.  An
> *************** tree gimple_fold_indirect_ref (tree);
> *** 1174,1179 ****
> --- 1178,1184 ----
>  /* In tree-ssa-structalias.c */
>  bool find_what_p_points_to (tree);
>  bool clobber_what_escaped (void);
> + void compute_call_used_vars (void);
>
>  /* In tree-ssa-live.c */
>  extern void remove_unused_locals (void);
> Index: trunk/gcc/tree-ssa-alias.c
> ===================================================================
> *** trunk.orig/gcc/tree-ssa-alias.c     2008-06-26 17:37:37.000000000 +0200
> --- trunk/gcc/tree-ssa-alias.c  2008-06-26 17:38:02.000000000 +0200
> *************** compute_tag_properties (void)
> *** 505,511 ****
>    VEC_free (tree, heap, taglist);
>  }
>
> ! /* Set up the initial variable clobbers and globalness.
>     When this function completes, only tags whose aliases need to be
>     clobbered will be set clobbered.  Tags clobbered because they
>     contain call clobbered vars are handled in compute_tag_properties.  */
> --- 505,511 ----
>    VEC_free (tree, heap, taglist);
>  }
>
> ! /* Set up the initial variable clobbers, call-uses and globalness.
>     When this function completes, only tags whose aliases need to be
>     clobbered will be set clobbered.  Tags clobbered because they
>     contain call clobbered vars are handled in compute_tag_properties.  */
> *************** set_initial_properties (struct alias_inf
> *** 543,548 ****
> --- 543,550 ----
>        pt_anything_mask |= ESCAPE_TO_CALL;
>      }
>
> +   compute_call_used_vars ();
> +
>    for (i = 0; VEC_iterate (tree, ai->processed_ptrs, i, ptr); i++)
>      {
>        struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
> *************** reset_alias_info (void)
> *** 2000,2005 ****
> --- 2002,2010 ----
>    /* There should be no call-clobbered variable left.  */
>    gcc_assert (bitmap_empty_p (gimple_call_clobbered_vars (cfun)));
>
> +   /* Clear the call-used variables.  */
> +   bitmap_clear (gimple_call_used_vars (cfun));
> +
>    /* Clear flow-sensitive points-to information from each SSA name.  */
>    for (i = 1; i < num_ssa_names; i++)
>      {
> Index: trunk/gcc/tree-ssa-operands.c
> ===================================================================
> *** trunk.orig/gcc/tree-ssa-operands.c  2008-06-26 17:37:37.000000000 +0200
> --- trunk/gcc/tree-ssa-operands.c       2008-06-26 18:13:07.000000000 +0200
> *************** add_call_clobber_ops (tree stmt, tree ca
> *** 1660,1666 ****
>    bitmap_iterator bi;
>    stmt_ann_t s_ann = stmt_ann (stmt);
>    bitmap not_read_b, not_written_b;
> !
>    /* If we created .GLOBAL_VAR earlier, just use it.  */
>    if (gimple_global_var (cfun))
>      {
> --- 1660,1669 ----
>    bitmap_iterator bi;
>    stmt_ann_t s_ann = stmt_ann (stmt);
>    bitmap not_read_b, not_written_b;
> !   tree call = get_call_expr_in (stmt);
> ! !   gcc_assert (!(call_expr_flags (call) & (ECF_PURE | ECF_CONST)));
> !
>    /* If we created .GLOBAL_VAR earlier, just use it.  */
>    if (gimple_global_var (cfun))
>      {
> *************** add_call_clobber_ops (tree stmt, tree ca
> *** 1674,1685 ****
>       or write that variable.  */
>    not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL;
>    not_written_b = callee ? ipa_reference_get_not_written_global (callee) :
> NULL; -
>    /* Add a VDEF operand for every call clobbered variable.  */
>    EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
>      {
>        tree var = referenced_var_lookup (u);
> -       unsigned int escape_mask = var_ann (var)->escape_mask;
>        tree real_var = var;
>        bool not_read;
>        bool not_written;
> --- 1677,1686 ----
> *************** add_call_clobber_ops (tree stmt, tree ca
> *** 1697,1720 ****
>
>        /* See if this variable is really clobbered by this function.  */
>
> -       /* Trivial case: Things escaping only to pure/const are not
> -        clobbered by non-pure-const, and only read by pure/const. */
> -       if ((escape_mask & ~(ESCAPE_TO_PURE_CONST)) == 0)
> -       {
> -         tree call = get_call_expr_in (stmt);
> -         if (call_expr_flags (call) & (ECF_CONST | ECF_PURE))
> -           {
> -             add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
> -             clobber_stats.unescapable_clobbers_avoided++;
> -             continue;
> -           }
> -         else
> -           {
> -             clobber_stats.unescapable_clobbers_avoided++;
> -             continue;
> -           }
> -       }
> -
>        if (not_written)
>        {
>          clobber_stats.static_write_clobbers_avoided++;
> --- 1698,1703 ----
> *************** add_call_read_ops (tree stmt, tree calle
> *** 1739,1756 ****
>    bitmap_iterator bi;
>    stmt_ann_t s_ann = stmt_ann (stmt);
>    bitmap not_read_b;
>
> !   /* if the function is not pure, it may reference memory.  Add
> !      a VUSE for .GLOBAL_VAR if it has been created.  See
> add_referenced_var
> !      for the heuristic used to decide whether to create .GLOBAL_VAR.  */
>    if (gimple_global_var (cfun))
>      {
>        tree var = gimple_global_var (cfun);
>        add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
>        return;
>      }
> - -   not_read_b = callee ? ipa_reference_get_not_read_global (callee) :
> NULL;
>
>    /* Add a VUSE for each call-clobbered variable.  */
>    EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
> --- 1722,1768 ----
>    bitmap_iterator bi;
>    stmt_ann_t s_ann = stmt_ann (stmt);
>    bitmap not_read_b;
> +   tree call = get_call_expr_in (stmt);
>
> !   /* Const functions do not reference memory.  */
> !   if (call_expr_flags (call) & ECF_CONST)
> !     return;
> ! !   not_read_b = callee ? ipa_reference_get_not_read_global (callee) :
> NULL;
> ! !   /* For pure functions we compute non-escaped uses separately.  */
> !   if (call_expr_flags (call) & ECF_PURE)
> !     EXECUTE_IF_SET_IN_BITMAP (gimple_call_used_vars (cfun), 0, u, bi)
> !       {
> !       tree var = referenced_var_lookup (u);
> !       tree real_var = var;
> !       bool not_read;
> ! !       if (unmodifiable_var_p (var))
> !         continue;
> ! !       not_read = not_read_b
> !           ? bitmap_bit_p (not_read_b, DECL_UID (real_var))
> !           : false;
> ! !       clobber_stats.readonly_clobbers++;
> ! !       /* See if this variable is really used by this function.  */
> !       if (!not_read)
> !         add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
> !       else
> !         clobber_stats.static_readonly_clobbers_avoided++;
> !       }
> ! !   /* Add a VUSE for .GLOBAL_VAR if it has been created.  See
> !      add_referenced_var for the heuristic used to decide whether to
> !      create .GLOBAL_VAR.  */
>    if (gimple_global_var (cfun))
>      {
>        tree var = gimple_global_var (cfun);
>        add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
>        return;
>      }
>
>    /* Add a VUSE for each call-clobbered variable.  */
>    EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
> Index: trunk/gcc/tree-ssa.c
> ===================================================================
> *** trunk.orig/gcc/tree-ssa.c   2008-06-26 17:37:37.000000000 +0200
> --- trunk/gcc/tree-ssa.c        2008-06-26 17:38:02.000000000 +0200
> *************** init_tree_ssa (struct function *fn)
> *** 937,942 ****
> --- 937,943 ----
>    fn->gimple_df->default_defs = htab_create_ggc (20, uid_ssaname_map_hash,
>                                                 uid_ssaname_map_eq, NULL);
>    fn->gimple_df->call_clobbered_vars = BITMAP_GGC_ALLOC ();
> +   fn->gimple_df->call_used_vars = BITMAP_GGC_ALLOC ();
>    fn->gimple_df->addressable_vars = BITMAP_GGC_ALLOC ();
>    init_ssanames (fn, 0);
>    init_phinodes ();
> *************** delete_tree_ssa (void)
> *** 1009,1014 ****
> --- 1010,1016 ----
>    htab_delete (cfun->gimple_df->default_defs);
>    cfun->gimple_df->default_defs = NULL;
>    cfun->gimple_df->call_clobbered_vars = NULL;
> +   cfun->gimple_df->call_used_vars = NULL;
>    cfun->gimple_df->addressable_vars = NULL;
>    cfun->gimple_df->modified_noreturn_calls = NULL;
>    if (gimple_aliases_computed_p (cfun))
> Index: trunk/gcc/tree-dfa.c
> ===================================================================
> *** trunk.orig/gcc/tree-dfa.c   2008-06-26 17:37:37.000000000 +0200
> --- trunk/gcc/tree-dfa.c        2008-06-26 17:38:02.000000000 +0200
> *************** remove_referenced_var (tree var)
> *** 746,751 ****
> --- 746,752 ----
>    unsigned int uid = DECL_UID (var);
>
>    clear_call_clobbered (var);
> +   bitmap_clear_bit (gimple_call_used_vars (cfun), uid);
>    if ((v_ann = var_ann (var)))
>      {
>        /* Preserve var_anns of globals, but clear their alias info.  */
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Track pure function call uses separately
  2008-06-26 20:35 ` Daniel Berlin
@ 2008-06-26 20:46   ` Richard Guenther
  2008-06-27 10:53   ` Richard Guenther
  1 sibling, 0 replies; 8+ messages in thread
From: Richard Guenther @ 2008-06-26 20:46 UTC (permalink / raw)
  To: Daniel Berlin; +Cc: gcc-patches, Diego Novillo

On Thu, 26 Jun 2008, Daniel Berlin wrote:

> On Thu, Jun 26, 2008 at 12:34 PM, Richard Guenther <rguenther@suse.de> wrote:
>>
>> This adds the ability (on top of
>> http://gcc.gnu.org/ml/gcc-patches/2008-06/msg01432.html - PING for that)
>
> I thought I okayed that one on IRC.
> I will do so explicitly in a moment.
>
>
>> to track the call-uses of pure function calls separately (globbed
>> per function though).  This fixes the regression on tree-ssa/pr24287.c
>> of the aforementioned patch and should in general reduce the amount
>> of call-clobbered variables for functions calling pure functions.
>
> Does this mean i can do the pure/const patch i had before and not have
> it break stuff?
> :)

No, it means the optimization is taken care of with this patch.

;)

Richard.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Track pure function call uses separately
  2008-06-26 20:35 ` Daniel Berlin
  2008-06-26 20:46   ` Richard Guenther
@ 2008-06-27 10:53   ` Richard Guenther
  2008-06-27 12:42     ` Richard Guenther
  2008-06-27 15:18     ` Daniel Berlin
  1 sibling, 2 replies; 8+ messages in thread
From: Richard Guenther @ 2008-06-27 10:53 UTC (permalink / raw)
  To: Daniel Berlin; +Cc: gcc-patches, Diego Novillo

On Thu, 26 Jun 2008, Daniel Berlin wrote:

> On Thu, Jun 26, 2008 at 12:34 PM, Richard Guenther <rguenther@suse.de> wrote:
>>
>> This adds the ability (on top of
>> http://gcc.gnu.org/ml/gcc-patches/2008-06/msg01432.html - PING for that)
>
> I thought I okayed that one on IRC.
> I will do so explicitly in a moment.

We have one "little" problem.  If we re-enable field-sensitive PTA
then the transitive closure we use for computing the ESCAPED set
is no longer equivalent to the reachability, which we really need
to compute.  Consider

struct Foo {
   int *p;
   int *q;
};

void __attribute__((noinline))
bar (int **x)
{
   struct Foo *f = (struct Foo *)x;
   *(f->q) = 0;
}

int foo(void)
{
   struct Foo f;
   int i = 1, j = 2;
   f.p = &i;
   f.q = &j;
   bar(&f.p);
   return j;
}

extern void abort (void);
int main()
{
   if (foo () != 0)
     abort ();
}

where in foo we end up with

ESCAPED = *ESCAPED
derefaddrtmp.30 = &ESCAPED
*ESCAPED = derefaddrtmp.30
derefaddrtmp.31 = &NONLOCAL
*ESCAPED = derefaddrtmp.31
f = &i
f.q = &j
ESCAPED = &f

and the solution

ESCAPED = { ESCAPED NONLOCAL f i }
f = { ESCAPED NONLOCAL i }
f.q = { j }

while f.q and j should also be escaped and f.q pointing to
ESCAPED NONLOCAL as well.

So what we would need here is a special deref constraint that
dereferences all subvars and use that for the ESCAPED = *ESCAPED
constraint.  Where can this be implemented?  I guess the solver
itself doesn't know about subvariables, but at constraint generation
time we cannot fix this either.  Computing the reachability after
PTA does also not work because then the PTA results would be wrong
again.

Ideas?  It looks like we would need to add constraints on-the-fly
during solving - is this possible at all?  (can you hint me at
where I would need to add those?)

Thanks,
Richard.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Track pure function call uses separately
  2008-06-27 10:53   ` Richard Guenther
@ 2008-06-27 12:42     ` Richard Guenther
  2008-06-27 15:33       ` Daniel Berlin
  2008-06-27 15:18     ` Daniel Berlin
  1 sibling, 1 reply; 8+ messages in thread
From: Richard Guenther @ 2008-06-27 12:42 UTC (permalink / raw)
  To: Daniel Berlin; +Cc: gcc-patches, Diego Novillo

On Fri, 27 Jun 2008, Richard Guenther wrote:

> On Thu, 26 Jun 2008, Daniel Berlin wrote:
>
>> On Thu, Jun 26, 2008 at 12:34 PM, Richard Guenther <rguenther@suse.de> 
>> wrote:
>>> 
>>> This adds the ability (on top of
>>> http://gcc.gnu.org/ml/gcc-patches/2008-06/msg01432.html - PING for that)
>> 
>> I thought I okayed that one on IRC.
>> I will do so explicitly in a moment.
>
> We have one "little" problem.  If we re-enable field-sensitive PTA
> then the transitive closure we use for computing the ESCAPED set
> is no longer equivalent to the reachability, which we really need
> to compute.  Consider
>
> struct Foo {
>  int *p;
>  int *q;
> };
>
> void __attribute__((noinline))
> bar (int **x)
> {
>  struct Foo *f = (struct Foo *)x;
>  *(f->q) = 0;
> }
>
> int foo(void)
> {
>  struct Foo f;
>  int i = 1, j = 2;
>  f.p = &i;
>  f.q = &j;
>  bar(&f.p);
>  return j;
> }
>
> extern void abort (void);
> int main()
> {
>  if (foo () != 0)
>    abort ();
> }
>
> where in foo we end up with
>
> ESCAPED = *ESCAPED
> derefaddrtmp.30 = &ESCAPED
> *ESCAPED = derefaddrtmp.30
> derefaddrtmp.31 = &NONLOCAL
> *ESCAPED = derefaddrtmp.31
> f = &i
> f.q = &j
> ESCAPED = &f
>
> and the solution
>
> ESCAPED = { ESCAPED NONLOCAL f i }
> f = { ESCAPED NONLOCAL i }
> f.q = { j }
>
> while f.q and j should also be escaped and f.q pointing to
> ESCAPED NONLOCAL as well.
>
> So what we would need here is a special deref constraint that
> dereferences all subvars and use that for the ESCAPED = *ESCAPED
> constraint.  Where can this be implemented?  I guess the solver
> itself doesn't know about subvariables, but at constraint generation
> time we cannot fix this either.  Computing the reachability after
> PTA does also not work because then the PTA results would be wrong
> again.
>
> Ideas?  It looks like we would need to add constraints on-the-fly
> during solving - is this possible at all?  (can you hint me at
> where I would need to add those?)

Ok, I have it "working" with something like the following.

Basically we need to treat 'reachability vars' specially, for
ESCAPED this is constraints of the form

  ESCAPED = x

(in the solution propagation propagate using set_union_all_fields)

  ESCAPED = &x

initially add all fields of x to ESCAPED.  Probably better done by
collapsing x - the solution of x will get all of ESCAPED through
the *ESCAPED = ESCAPED constraint anyway, so no point in processing
the fields of x separately.  (Ideally we would collapse during
solving, but that doesn't look easily doable(?))

  ESCAPED = *ESCAPED

I hacked do_sd_constraint to handle this special case - we need
to add all fields the solution in ESCAPED can reach.

So, it gets somewhat ugly, and of course I am not sure that
variable unification will not get in our way ... (when exactly
are two variables unified?)

Thanks,
Richard.


Index: trunk/gcc/tree-ssa-structalias.c
===================================================================
--- trunk.orig/gcc/tree-ssa-structalias.c	2008-06-27 14:32:22.000000000 +0200
+++ trunk/gcc/tree-ssa-structalias.c	2008-06-27 14:24:27.000000000 +0200
@@ -262,6 +262,7 @@ struct variable_info
  typedef struct variable_info *varinfo_t;

  static varinfo_t first_vi_for_offset (varinfo_t, unsigned HOST_WIDE_INT);
+static varinfo_t lookup_vi_for_tree (tree);

  /* Pool of variable info structures.  */
  static alloc_pool variable_info_pool;
@@ -1114,7 +1115,18 @@ build_succ_graph (void)
  	  /* x = &y */
  	  gcc_assert (find (get_varinfo_fc (rhs.var)->id)
  		      == get_varinfo_fc (rhs.var)->id);
-	  bitmap_set_bit (get_varinfo (lhsvar)->solution, rhsvar);
+	  if (lhs.var == escaped_id)
+	    {
+	      /* We might want to simply collapse the rhs variable at this
+	         point.
+		 ???  This would need to happen at the point we generate
+		 the constraint.  */
+	      varinfo_t vi = lookup_vi_for_tree (get_varinfo_fc (rhs.var)->decl);
+	      for (; vi != NULL; vi = vi->next)
+		bitmap_set_bit (get_varinfo (lhsvar)->solution, vi->id);
+	    }
+	  else
+	    bitmap_set_bit (get_varinfo (lhsvar)->solution, rhsvar);
  	}
        else if (lhsvar > anything_id
  	       && lhsvar != rhsvar && lhs.offset == 0 && rhs.offset == 0)
@@ -1400,13 +1412,45 @@ do_sd_constraint (constraint_graph_t gra
    unsigned int j;
    bitmap_iterator bi;

- if (bitmap_bit_p (delta, anything_id))
-   {
-     flag = !bitmap_bit_p (sol, anything_id);
-     if (flag)
-       bitmap_set_bit (sol, anything_id);
-     goto done;
-   }
+  if (bitmap_bit_p (delta, anything_id))
+    {
+      flag = !bitmap_bit_p (sol, anything_id);
+      if (flag)
+	bitmap_set_bit (sol, anything_id);
+      goto done;
+    }
+
+  /* For x = *ESCAPED we interpret the deref as dereferencing all fields
+     of the ESCAPED solution variables.
+     ???  The question is if this works reliably if subvars are
+     collapsed.  We might not see the original variable in delta and thus
+     not reach other fields.  */
+  if (c->rhs.var == escaped_id)
+    {
+      EXECUTE_IF_SET_IN_BITMAP (delta, 0, j, bi)
+	{
+	  varinfo_t v = lookup_vi_for_tree (get_varinfo (j)->decl);
+
+	  /* Only process one field as representative.  */
+	  if (v != get_varinfo (j)
+	      && bitmap_bit_p (delta, v->id))
+	    continue;
+
+	  for (; v != NULL; v = v->next)
+	    {
+	      unsigned int t = find (v->id);
+ 
+	      /* Adding edges from the special vars is pointless.
+		 They don't have sets that can change.  */
+	      if (get_varinfo (t)->is_special_var)
+		flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
+	      else if (add_graph_edge (graph, lhs, t))
+		flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
+	    }
+	}
+      goto done;
+    }
+
    /* For each variable j in delta (Sol(y)), add
       an edge in the graph from j to x, and union Sol(j) into Sol(x).  */
    EXECUTE_IF_SET_IN_BITMAP (delta, 0, j, bi)
@@ -1564,6 +1608,30 @@ do_complex_constraint (constraint_graph_
      }
  }

+static bool
+set_union_all_fields (bitmap tmp, bitmap pts)
+{
+  bitmap_iterator bi;
+  unsigned i;
+  bool flag = false;
+
+  /* For each variable in pts add all its decls variables to tmp.  */
+  EXECUTE_IF_SET_IN_BITMAP (pts, 0, i, bi)
+    {
+      varinfo_t vi = lookup_vi_for_tree (get_varinfo (i)->decl);
+      if (vi != get_varinfo (i)
+	  && bitmap_bit_p (pts, vi->id))
+	continue;
+      for (; vi != NULL; vi = vi->next)
+	{
+	  flag |= bitmap_bit_p (tmp, vi->id);
+	  bitmap_set_bit (tmp, vi->id);
+	}
+    }
+
+  return flag;
+}
+
  /* Initialize and return a new SCC info structure.  */

  static struct scc_info *
@@ -2378,7 +2446,12 @@ solve_graph (constraint_graph_t graph)
  		      if (to == i)
  			continue;

-		      flag = set_union_with_increment (tmp, pts, 0);
+		      /* If this is a copy to the reachability set
+		         ESCAPED, process it specially.  */
+		      if (to == escaped_id)
+			flag = set_union_all_fields (tmp, pts);
+		      else
+			flag = set_union_with_increment (tmp, pts, 0);

  		      if (flag)
  			{

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Track pure function call uses separately
  2008-06-27 10:53   ` Richard Guenther
  2008-06-27 12:42     ` Richard Guenther
@ 2008-06-27 15:18     ` Daniel Berlin
  1 sibling, 0 replies; 8+ messages in thread
From: Daniel Berlin @ 2008-06-27 15:18 UTC (permalink / raw)
  To: Richard Guenther; +Cc: gcc-patches, Diego Novillo

On Fri, Jun 27, 2008 at 6:07 AM, Richard Guenther <rguenther@suse.de> wrote:
> On Thu, 26 Jun 2008, Daniel Berlin wrote:
>
>> On Thu, Jun 26, 2008 at 12:34 PM, Richard Guenther <rguenther@suse.de>
>> wrote:
>>>
>>> This adds the ability (on top of
>>> http://gcc.gnu.org/ml/gcc-patches/2008-06/msg01432.html - PING for that)
>>
>> I thought I okayed that one on IRC.
>> I will do so explicitly in a moment.
>
> We have one "little" problem.  If we re-enable field-sensitive PTA
> then the transitive closure we use for computing the ESCAPED set
> is no longer equivalent to the reachability, which we really need
> to compute.  Consider
>
> struct Foo {
>  int *p;
>  int *q;
> };
>
> void __attribute__((noinline))
> bar (int **x)
> {
>  struct Foo *f = (struct Foo *)x;
>  *(f->q) = 0;
> }
>
> int foo(void)
> {
>  struct Foo f;
>  int i = 1, j = 2;
>  f.p = &i;
>  f.q = &j;
>  bar(&f.p);
>  return j;
> }
>
> extern void abort (void);
> int main()
> {
>  if (foo () != 0)
>    abort ();
> }
>
> where in foo we end up with
>
> ESCAPED = *ESCAPED
> derefaddrtmp.30 = &ESCAPED
> *ESCAPED = derefaddrtmp.30
> derefaddrtmp.31 = &NONLOCAL
> *ESCAPED = derefaddrtmp.31
> f = &i
> f.q = &j
> ESCAPED = &f
>
> and the solution
>
> ESCAPED = { ESCAPED NONLOCAL f i }
> f = { ESCAPED NONLOCAL i }
> f.q = { j }
>
> while f.q and j should also be escaped and f.q pointing to
> ESCAPED NONLOCAL as well.


Hmmm.
Ya.
You will have to special case escape contraints in the solver if you
want it to cause whole-field escapes.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Track pure function call uses separately
  2008-06-27 12:42     ` Richard Guenther
@ 2008-06-27 15:33       ` Daniel Berlin
  0 siblings, 0 replies; 8+ messages in thread
From: Daniel Berlin @ 2008-06-27 15:33 UTC (permalink / raw)
  To: Richard Guenther; +Cc: gcc-patches, Diego Novillo

> Ok, I have it "working" with something like the following.
>
> Basically we need to treat 'reachability vars' specially, for
> ESCAPED this is constraints of the form

FWIW, this problem only occurs if you try to compute escaping at the
same time as points-to.
I understand it is probably not worth computing escaping separately
(since it would more or less just double the time we are taking), i'm
just saying.
> initially add all fields of x to ESCAPED.  Probably better done by
> collapsing x - the solution of x will get all of ESCAPED through
> the *ESCAPED = ESCAPED constraint anyway, so no point in processing
> the fields of x separately.  (Ideally we would collapse during
> solving, but that doesn't look easily doable(?))

You can unify and collapse any variables you like during either
solving or prior to solving.
Just unify the fields to the base.

Unifying more variables will never cause the points-to sets to be
wrong, only less precise.
:)

In fact, if you were to unify every RHS and LHS where you see "LHS =
RHS", you would end up with the same results as steensgaard's
analysis.

>
>  ESCAPED = *ESCAPED
>
> I hacked do_sd_constraint to handle this special case - we need
> to add all fields the solution in ESCAPED can reach.
>
> So, it gets somewhat ugly, and of course I am not sure that
> variable unification will not get in our way ... (when exactly
> are two variables unified?)

Two variables are unified prior to solving when we can prove they will
end up with the same points-to set based on the constraints.
This is a subset of "when they will have the same points-to set".
In theory, you should expect any variables that, based only on the
constraints, will end up with the same points-to set, to be unified.
They may not be (due to difficulties in proving it), but it is not
"wrong" for it to happen.

This is the limit of what we do, because anything more could affect
precision of analysis.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Track pure function call uses separately
  2008-06-26 18:40 [PATCH] Track pure function call uses separately Richard Guenther
  2008-06-26 20:35 ` Daniel Berlin
@ 2008-06-28 13:26 ` Richard Guenther
  1 sibling, 0 replies; 8+ messages in thread
From: Richard Guenther @ 2008-06-28 13:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: Diego Novillo, Daniel Berlin

On Thu, 26 Jun 2008, Richard Guenther wrote:

>
> This adds the ability (on top of
> http://gcc.gnu.org/ml/gcc-patches/2008-06/msg01432.html - PING for that)
> to track the call-uses of pure function calls separately (globbed
> per function though).  This fixes the regression on tree-ssa/pr24287.c
> of the aforementioned patch and should in general reduce the amount
> of call-clobbered variables for functions calling pure functions.
>
> Bootstrapped on x86_64-unknown-linux-gnu, regtesting in progress.
>
> Ok for mainline?

Approved by Danny on IRC.  I re-bootstrapped/tested the following
on x86_64-unknown-linux-gnu, it has the same optimization as I
added for ESCAPED.

Installed to the trunk.

Richard.

2008-06-26  Richard Guenther  <rguenther@suse.de>

 	* tree-ssa-structalias.c (callused_id, var_callused,
 	callused_tree): Add.
 	(handle_pure_call): New function.
 	(find_func_aliases): Call it.
 	(find_what_p_points_to): Handle the call-used set.
 	(clobber_what_escaped): Likewise.
 	(compute_call_used_vars): New function.
 	(init_base_vars): Init the call-used variable.
 	(do_sd_constraint): Do not propagate the solution from CALLUSED
 	but use CALLUSED as a placeholder.
 	(solve_graph): Likewise.
 	* tree-flow-inline.h (gimple_call_used_vars): New function.
 	* tree-flow.h (struct gimple_df): Add call_used_vars bitmap.
 	(compute_call_used_vars): Declare.
 	* tree-ssa-alias.c (set_initial_properties): Call
 	compute_call_used_vars.
 	(reset_alias_info): Clear call-used variables.
 	(add_call_clobber_ops): Assert we are not called for const/pure
 	functions.  Remove handling of them.
 	(add_call_read_ops): Handle pure functions by adding the
 	call-used set of variables as VUSEs.
 	* tree-ssa.c (init_tree_ssa): Allocate call-used bitmap.
 	(delete_tree_ssa): Free it.
 	* tree-dfa.c (remove_referenced_var): Clear the var from the
 	call-used bitmap.

 	* gcc.dg/tree-ssa/pr24287.c: Remove XFAIL.

Index: trunk/gcc/tree-ssa-structalias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-structalias.c	2008-06-27 20:51:47.000000000 +0200
--- trunk/gcc/tree-ssa-structalias.c	2008-06-28 13:29:55.000000000 +0200
*************** get_varinfo_fc (unsigned int n)
*** 296,302 ****

   /* Static IDs for the special variables.  */
   enum { nothing_id = 0, anything_id = 1, readonly_id = 2,
!        escaped_id = 3, nonlocal_id = 4, integer_id = 5 };

   /* Variable that represents the unknown pointer.  */
   static varinfo_t var_anything;
--- 296,302 ----

   /* Static IDs for the special variables.  */
   enum { nothing_id = 0, anything_id = 1, readonly_id = 2,
!        escaped_id = 3, nonlocal_id = 4, callused_id = 5, integer_id = 6 };

   /* Variable that represents the unknown pointer.  */
   static varinfo_t var_anything;
*************** static tree escaped_tree;
*** 318,323 ****
--- 318,327 ----
   static varinfo_t var_nonlocal;
   static tree nonlocal_tree;

+ /* Variable that represents call-used memory.  */
+ static varinfo_t var_callused;
+ static tree callused_tree;
+
   /* Variable that represents integers.  This is used for when people do things
      like &0->a.b.  */
   static varinfo_t var_integer;
*************** do_sd_constraint (constraint_graph_t gra
*** 1429,1439 ****
   	  if (get_varinfo (t)->is_special_var)
   	    flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
   	  /* Merging the solution from ESCAPED needlessly increases
! 	     the set.  Use ESCAPED as representative instead.  */
! 	  else if (get_varinfo (t)->id == escaped_id
   		   && !bitmap_bit_p (sol, get_varinfo (t)->id))
   	    {
! 	      bitmap_set_bit (sol, escaped_id);
   	      flag = true;
   	    }
   	  else if (add_graph_edge (graph, lhs, t))
--- 1433,1445 ----
   	  if (get_varinfo (t)->is_special_var)
   	    flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
   	  /* Merging the solution from ESCAPED needlessly increases
! 	     the set.  Use ESCAPED as representative instead.
! 	     Same for CALLUSED.  */
! 	  else if ((get_varinfo (t)->id == escaped_id
! 		    || get_varinfo (t)->id == callused_id)
   		   && !bitmap_bit_p (sol, get_varinfo (t)->id))
   	    {
! 	      bitmap_set_bit (sol, get_varinfo (t)->id);
   	      flag = true;
   	    }
   	  else if (add_graph_edge (graph, lhs, t))
*************** solve_graph (constraint_graph_t graph)
*** 2369,2376 ****
   	      solution_empty = bitmap_empty_p (solution);

   	      if (!solution_empty
! 		  /* Do not propagate the ESCAPED solution.  */
! 		  && i != escaped_id)
   		{
   		  bitmap_iterator bi;

--- 2375,2383 ----
   	      solution_empty = bitmap_empty_p (solution);

   	      if (!solution_empty
! 		  /* Do not propagate the ESCAPED/CALLUSED solutions.  */
! 		  && i != escaped_id
! 		  && i != callused_id)
   		{
   		  bitmap_iterator bi;

*************** handle_const_call (tree stmt)
*** 3702,3707 ****
--- 3709,3769 ----
     VEC_free (ce_s, heap, lhsc);
   }

+ /* For non-IPA mode, generate constraints necessary for a call to a
+    pure function in statement STMT.  */
+ 
+ static void
+ handle_pure_call (tree stmt)
+ {
+   tree call = get_call_expr_in (stmt);
+   tree arg;
+   call_expr_arg_iterator iter;
+ 
+   /* Memory reached from pointer arguments is call-used.  */
+   FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
+     if (could_have_pointers (arg))
+       make_constraint_to (callused_id, arg);
+ 
+   /* The static chain is used as well.  */
+   if (CALL_EXPR_STATIC_CHAIN (call))
+     make_constraint_to (callused_id, CALL_EXPR_STATIC_CHAIN (call));
+ 
+   /* If the call returns a pointer it may point to reachable memory
+      from the arguments.  */
+   if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+       && could_have_pointers (GIMPLE_STMT_OPERAND (stmt, 0)))
+     {
+       tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
+       VEC(ce_s, heap) *lhsc = NULL;
+       struct constraint_expr rhsc;
+       struct constraint_expr *lhsp;
+       unsigned j;
+ 
+       get_constraint_for (lhs, &lhsc);
+ 
+       /* If this is a nested function then it can return anything.  */
+       if (CALL_EXPR_STATIC_CHAIN (call))
+ 	{
+ 	  rhsc.var = anything_id;
+ 	  rhsc.offset = 0;
+ 	  rhsc.type = ADDRESSOF;
+ 	  for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ 	    process_constraint_1 (new_constraint (*lhsp, rhsc), true);
+ 	  VEC_free (ce_s, heap, lhsc);
+ 	  return;
+ 	}
+ 
+       /* Else just add the call-used memory here.  Escaped variables
+          and globals will be dealt with in handle_lhs_call.  */
+       rhsc.var = callused_id;
+       rhsc.offset = 0;
+       rhsc.type = ADDRESSOF;
+       for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ 	process_constraint_1 (new_constraint (*lhsp, rhsc), true);
+       VEC_free (ce_s, heap, lhsc);
+     }
+ }
+
   /* 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 (tree origt)
*** 3778,3783 ****
--- 3840,3852 ----
   		  && could_have_pointers (GIMPLE_STMT_OPERAND (t, 1)))
   		handle_const_call (t);
   	    }
+ 	  else if (flags & ECF_PURE)
+ 	    {
+ 	      handle_pure_call (t);
+ 	      if (TREE_CODE (t) == GIMPLE_MODIFY_STMT
+ 		  && could_have_pointers (GIMPLE_STMT_OPERAND (t, 1)))
+ 		handle_lhs_call (GIMPLE_STMT_OPERAND (t, 0));
+ 	    }
   	  /* Pure functions can return addresses in and of memory
   	     reachable from their arguments, but they are not an escape
   	     point for reachable memory of their arguments.  But as we
*************** find_what_p_points_to (tree p)
*** 4940,4946 ****
   		    pi->pt_null = 1;
   		  else if (vi->id == anything_id
   			   || vi->id == nonlocal_id
! 			   || vi->id == escaped_id)
   		    was_pt_anything = 1;
   		  else if (vi->id == readonly_id)
   		    was_pt_anything = 1;
--- 5009,5016 ----
   		    pi->pt_null = 1;
   		  else if (vi->id == anything_id
   			   || vi->id == nonlocal_id
! 			   || vi->id == escaped_id
! 			   || vi->id == callused_id)
   		    was_pt_anything = 1;
   		  else if (vi->id == readonly_id)
   		    was_pt_anything = 1;
*************** clobber_what_escaped (void)
*** 5007,5012 ****
--- 5077,5091 ----
        variable for escaped_id.  */
     vi = get_varinfo (find (escaped_id));

+   /* If call-used memory escapes we need to include it in the
+      set of escaped variables.  This can happen if a pure
+      function returns a pointer and this pointer escapes.  */
+   if (bitmap_bit_p (vi->solution, callused_id))
+     {
+       varinfo_t cu_vi = get_varinfo (find (callused_id));
+       bitmap_ior_into (vi->solution, cu_vi->solution);
+     }
+
     /* Mark variables in the solution call-clobbered.  */
     EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
       {
*************** clobber_what_escaped (void)
*** 5036,5041 ****
--- 5115,5168 ----
     return true;
   }

+ /* Compute the call-used variables.  */
+ 
+ void
+ compute_call_used_vars (void)
+ {
+   varinfo_t vi;
+   unsigned int i;
+   bitmap_iterator bi;
+   bool has_anything_id = false;
+ 
+   if (!have_alias_info)
+     return;
+ 
+   /* This variable may have been collapsed, let's get the real
+      variable for escaped_id.  */
+   vi = get_varinfo (find (callused_id));
+ 
+   /* Mark variables in the solution call-clobbered.  */
+   EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
+     {
+       varinfo_t vi = get_varinfo (i);
+ 
+       if (vi->is_artificial_var)
+ 	{
+ 	  /* For anything_id and integer_id we need to make
+ 	     all local addressable vars call-used.  */
+ 	  if (vi->id == anything_id
+ 	      || vi->id == integer_id)
+ 	    has_anything_id = true;
+ 	}
+ 
+       /* Only artificial heap-vars are further interesting.  */
+       if (vi->is_artificial_var && !vi->is_heap_var)
+ 	continue;
+ 
+       if ((TREE_CODE (vi->decl) == VAR_DECL
+ 	   || TREE_CODE (vi->decl) == PARM_DECL
+ 	   || TREE_CODE (vi->decl) == RESULT_DECL)
+ 	  && !unmodifiable_var_p (vi->decl))
+ 	bitmap_set_bit (gimple_call_used_vars (cfun), DECL_UID (vi->decl));
+     }
+ 
+   /* If anything is call-used, add all addressable locals to the set.  */
+   if (has_anything_id)
+     bitmap_ior_into (gimple_call_used_vars (cfun),
+ 		     gimple_addressable_vars (cfun));
+ }
+

   /* Dump points-to information to OUTFILE.  */

*************** init_base_vars (void)
*** 5193,5198 ****
--- 5320,5346 ----
     rhs.offset = 0;
     process_constraint (new_constraint (lhs, rhs));

+   /* Create the CALLUSED variable, used to represent the set of call-used
+      memory.  */
+   callused_tree = create_tmp_var_raw (void_type_node, "CALLUSED");
+   var_callused = new_var_info (callused_tree, callused_id, "CALLUSED");
+   insert_vi_for_tree (callused_tree, var_callused);
+   var_callused->is_artificial_var = 1;
+   var_callused->offset = 0;
+   var_callused->size = ~0;
+   var_callused->fullsize = ~0;
+   var_callused->is_special_var = 0;
+   VEC_safe_push (varinfo_t, heap, varmap, var_callused);
+ 
+   /* CALLUSED = *CALLUSED, because call-used is may-deref'd at calls, etc.  */
+   lhs.type = SCALAR;
+   lhs.var = callused_id;
+   lhs.offset = 0;
+   rhs.type = DEREF;
+   rhs.var = callused_id;
+   rhs.offset = 0;
+   process_constraint_1 (new_constraint (lhs, rhs), true);
+
     /* Create the INTEGER variable, used to represent that a variable points
        to an INTEGER.  */
     integer_tree = create_tmp_var_raw (void_type_node, "INTEGER");
Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c
===================================================================
*** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c	2008-06-27 20:51:47.000000000 +0200
--- trunk/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c	2008-06-28 13:28:00.000000000 +0200
*************** int g(void)
*** 21,25 ****
       link_error ();
     return t2 == 2;
   }
! /* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" { xfail *-*-* } } } */
   /* { dg-final { cleanup-tree-dump "optimized" } } */
--- 21,25 ----
       link_error ();
     return t2 == 2;
   }
! /* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" } } */
   /* { dg-final { cleanup-tree-dump "optimized" } } */
Index: trunk/gcc/tree-flow-inline.h
===================================================================
*** trunk.orig/gcc/tree-flow-inline.h	2008-06-27 12:07:55.000000000 +0200
--- trunk/gcc/tree-flow-inline.h	2008-06-28 13:28:00.000000000 +0200
*************** gimple_call_clobbered_vars (const struct
*** 66,71 ****
--- 66,80 ----
     return fun->gimple_df->call_clobbered_vars;
   }

+ /* Call-used variables in the function.  If bit I is set, then
+    REFERENCED_VARS (I) is call-used at pure function call-sites.  */
+ static inline bitmap
+ gimple_call_used_vars (const struct function *fun)
+ {
+   gcc_assert (fun && fun->gimple_df);
+   return fun->gimple_df->call_used_vars;
+ }
+
   /* Array of all variables referenced in the function.  */
   static inline htab_t
   gimple_referenced_vars (const struct function *fun)
Index: trunk/gcc/tree-flow.h
===================================================================
*** trunk.orig/gcc/tree-flow.h	2008-06-27 20:51:47.000000000 +0200
--- trunk/gcc/tree-flow.h	2008-06-28 13:28:00.000000000 +0200
*************** struct gimple_df GTY(())
*** 162,167 ****
--- 162,171 ----
        REFERENCED_VARS (I) is call-clobbered.  */
     bitmap call_clobbered_vars;

+   /* Call-used variables in the function.  If bit I is set, then
+      REFERENCED_VARS (I) is call-used at pure function call-sites.  */
+   bitmap call_used_vars;
+
     /* Addressable variables in the function.  If bit I is set, then
        REFERENCED_VARS (I) has had its address taken.  Note that
        CALL_CLOBBERED_VARS and ADDRESSABLE_VARS are not related.  An
*************** tree gimple_fold_indirect_ref (tree);
*** 1174,1179 ****
--- 1178,1184 ----
   /* In tree-ssa-structalias.c */
   bool find_what_p_points_to (tree);
   bool clobber_what_escaped (void);
+ void compute_call_used_vars (void);

   /* In tree-ssa-live.c */
   extern void remove_unused_locals (void);
Index: trunk/gcc/tree-ssa-alias.c
===================================================================
*** trunk.orig/gcc/tree-ssa-alias.c	2008-06-27 20:51:47.000000000 +0200
--- trunk/gcc/tree-ssa-alias.c	2008-06-28 13:28:00.000000000 +0200
*************** compute_tag_properties (void)
*** 505,511 ****
     VEC_free (tree, heap, taglist);
   }

! /* Set up the initial variable clobbers and globalness.
      When this function completes, only tags whose aliases need to be
      clobbered will be set clobbered.  Tags clobbered because they
      contain call clobbered vars are handled in compute_tag_properties.  */
--- 505,511 ----
     VEC_free (tree, heap, taglist);
   }

! /* Set up the initial variable clobbers, call-uses and globalness.
      When this function completes, only tags whose aliases need to be
      clobbered will be set clobbered.  Tags clobbered because they
      contain call clobbered vars are handled in compute_tag_properties.  */
*************** set_initial_properties (struct alias_inf
*** 543,548 ****
--- 543,550 ----
         pt_anything_mask |= ESCAPE_TO_CALL;
       }

+   compute_call_used_vars ();
+
     for (i = 0; VEC_iterate (tree, ai->processed_ptrs, i, ptr); i++)
       {
         struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
*************** reset_alias_info (void)
*** 2000,2005 ****
--- 2002,2010 ----
     /* There should be no call-clobbered variable left.  */
     gcc_assert (bitmap_empty_p (gimple_call_clobbered_vars (cfun)));

+   /* Clear the call-used variables.  */
+   bitmap_clear (gimple_call_used_vars (cfun));
+
     /* Clear flow-sensitive points-to information from each SSA name.  */
     for (i = 1; i < num_ssa_names; i++)
       {
Index: trunk/gcc/tree-ssa-operands.c
===================================================================
*** trunk.orig/gcc/tree-ssa-operands.c	2008-06-27 12:07:55.000000000 +0200
--- trunk/gcc/tree-ssa-operands.c	2008-06-28 13:28:00.000000000 +0200
*************** add_call_clobber_ops (tree stmt, tree ca
*** 1660,1666 ****
     bitmap_iterator bi;
     stmt_ann_t s_ann = stmt_ann (stmt);
     bitmap not_read_b, not_written_b;
!
     /* If we created .GLOBAL_VAR earlier, just use it.  */
     if (gimple_global_var (cfun))
       {
--- 1660,1669 ----
     bitmap_iterator bi;
     stmt_ann_t s_ann = stmt_ann (stmt);
     bitmap not_read_b, not_written_b;
!   tree call = get_call_expr_in (stmt);
! 
!   gcc_assert (!(call_expr_flags (call) & (ECF_PURE | ECF_CONST)));
!
     /* If we created .GLOBAL_VAR earlier, just use it.  */
     if (gimple_global_var (cfun))
       {
*************** add_call_clobber_ops (tree stmt, tree ca
*** 1674,1685 ****
        or write that variable.  */
     not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL;
     not_written_b = callee ? ipa_reference_get_not_written_global (callee) : NULL; 
-
     /* Add a VDEF operand for every call clobbered variable.  */
     EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
       {
         tree var = referenced_var_lookup (u);
-       unsigned int escape_mask = var_ann (var)->escape_mask;
         tree real_var = var;
         bool not_read;
         bool not_written;
--- 1677,1686 ----
*************** add_call_clobber_ops (tree stmt, tree ca
*** 1697,1720 ****

         /* See if this variable is really clobbered by this function.  */

-       /* Trivial case: Things escaping only to pure/const are not
- 	 clobbered by non-pure-const, and only read by pure/const. */
-       if ((escape_mask & ~(ESCAPE_TO_PURE_CONST)) == 0)
- 	{
- 	  tree call = get_call_expr_in (stmt);
- 	  if (call_expr_flags (call) & (ECF_CONST | ECF_PURE))
- 	    {
- 	      add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
- 	      clobber_stats.unescapable_clobbers_avoided++;
- 	      continue;
- 	    }
- 	  else
- 	    {
- 	      clobber_stats.unescapable_clobbers_avoided++;
- 	      continue;
- 	    }
- 	}
-
         if (not_written)
   	{
   	  clobber_stats.static_write_clobbers_avoided++;
--- 1698,1703 ----
*************** add_call_read_ops (tree stmt, tree calle
*** 1739,1756 ****
     bitmap_iterator bi;
     stmt_ann_t s_ann = stmt_ann (stmt);
     bitmap not_read_b;

!   /* if the function is not pure, it may reference memory.  Add
!      a VUSE for .GLOBAL_VAR if it has been created.  See add_referenced_var
!      for the heuristic used to decide whether to create .GLOBAL_VAR.  */
     if (gimple_global_var (cfun))
       {
         tree var = gimple_global_var (cfun);
         add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
         return;
       }
- 
-   not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL;

     /* Add a VUSE for each call-clobbered variable.  */
     EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
--- 1722,1768 ----
     bitmap_iterator bi;
     stmt_ann_t s_ann = stmt_ann (stmt);
     bitmap not_read_b;
+   tree call = get_call_expr_in (stmt);

!   /* Const functions do not reference memory.  */
!   if (call_expr_flags (call) & ECF_CONST)
!     return;
! 
!   not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL;
! 
!   /* For pure functions we compute non-escaped uses separately.  */
!   if (call_expr_flags (call) & ECF_PURE)
!     EXECUTE_IF_SET_IN_BITMAP (gimple_call_used_vars (cfun), 0, u, bi)
!       {
! 	tree var = referenced_var_lookup (u);
! 	tree real_var = var;
! 	bool not_read;
! 
! 	if (unmodifiable_var_p (var))
! 	  continue;
! 
! 	not_read = not_read_b
! 	    ? bitmap_bit_p (not_read_b, DECL_UID (real_var))
! 	    : false;
! 
! 	clobber_stats.readonly_clobbers++;
! 
! 	/* See if this variable is really used by this function.  */
! 	if (!not_read)
! 	  add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
! 	else
! 	  clobber_stats.static_readonly_clobbers_avoided++;
!       }
! 
!   /* Add a VUSE for .GLOBAL_VAR if it has been created.  See
!      add_referenced_var for the heuristic used to decide whether to
!      create .GLOBAL_VAR.  */
     if (gimple_global_var (cfun))
       {
         tree var = gimple_global_var (cfun);
         add_virtual_operand (var, s_ann, opf_use, NULL, 0, -1, true);
         return;
       }

     /* Add a VUSE for each call-clobbered variable.  */
     EXECUTE_IF_SET_IN_BITMAP (gimple_call_clobbered_vars (cfun), 0, u, bi)
Index: trunk/gcc/tree-ssa.c
===================================================================
*** trunk.orig/gcc/tree-ssa.c	2008-06-27 12:07:55.000000000 +0200
--- trunk/gcc/tree-ssa.c	2008-06-28 13:28:00.000000000 +0200
*************** init_tree_ssa (struct function *fn)
*** 937,942 ****
--- 937,943 ----
     fn->gimple_df->default_defs = htab_create_ggc (20, uid_ssaname_map_hash,
   				                 uid_ssaname_map_eq, NULL);
     fn->gimple_df->call_clobbered_vars = BITMAP_GGC_ALLOC ();
+   fn->gimple_df->call_used_vars = BITMAP_GGC_ALLOC ();
     fn->gimple_df->addressable_vars = BITMAP_GGC_ALLOC ();
     init_ssanames (fn, 0);
     init_phinodes ();
*************** delete_tree_ssa (void)
*** 1009,1014 ****
--- 1010,1016 ----
     htab_delete (cfun->gimple_df->default_defs);
     cfun->gimple_df->default_defs = NULL;
     cfun->gimple_df->call_clobbered_vars = NULL;
+   cfun->gimple_df->call_used_vars = NULL;
     cfun->gimple_df->addressable_vars = NULL;
     cfun->gimple_df->modified_noreturn_calls = NULL;
     if (gimple_aliases_computed_p (cfun))
Index: trunk/gcc/tree-dfa.c
===================================================================
*** trunk.orig/gcc/tree-dfa.c	2008-06-27 12:07:55.000000000 +0200
--- trunk/gcc/tree-dfa.c	2008-06-28 13:28:00.000000000 +0200
*************** remove_referenced_var (tree var)
*** 746,751 ****
--- 746,752 ----
     unsigned int uid = DECL_UID (var);

     clear_call_clobbered (var);
+   bitmap_clear_bit (gimple_call_used_vars (cfun), uid);
     if ((v_ann = var_ann (var)))
       {
         /* Preserve var_anns of globals, but clear their alias info.  */

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-06-28 13:15 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-06-26 18:40 [PATCH] Track pure function call uses separately Richard Guenther
2008-06-26 20:35 ` Daniel Berlin
2008-06-26 20:46   ` Richard Guenther
2008-06-27 10:53   ` Richard Guenther
2008-06-27 12:42     ` Richard Guenther
2008-06-27 15:33       ` Daniel Berlin
2008-06-27 15:18     ` Daniel Berlin
2008-06-28 13:26 ` 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).