[PATCH] Track pure function call uses separately

Daniel Berlin dberlin@dberlin.org
Thu Jun 26 20:35:00 GMT 2008


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



More information about the Gcc-patches mailing list