This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] Track pure function call uses separately
- From: "Daniel Berlin" <dberlin at dberlin dot org>
- To: "Richard Guenther" <rguenther at suse dot de>
- Cc: gcc-patches at gcc dot gnu dot org, "Diego Novillo" <dnovillo at google dot com>
- Date: Thu, 26 Jun 2008 16:33:58 -0400
- Subject: Re: [PATCH] Track pure function call uses separately
- References: <alpine.LNX.1.10.0806261834050.8853@zhemvz.fhfr.qr>
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. */
>