[PATCH] Track pure function call uses separately

Richard Guenther rguenther@suse.de
Thu Jun 26 18:40:00 GMT 2008


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.  */



More information about the Gcc-patches mailing list