[PATCH][RFC] Make pure/const call clobber analysis context-sensitive

Richard Guenther richard.guenther@gmail.com
Tue Apr 13 09:53:00 GMT 2010


On Thu, Nov 19, 2009 at 6:10 PM, Richard Guenther <rguenther@suse.de> wrote:
>
> This uses the context sensitive information on call statements to
> provide finer granularity for pure and const function calls.
>
> Lightly tested sofar, but nothing non-obvious.  Queued for stage1.

Re-bootstrapped and tested on x86_64-unknown-linux-gnu, applied
to trunk as r158260.

Richard.

> Richard.
>
> 2009-11-19  Richard Guenther  <rguenther@suse.de>
>
>        * tree-ssa-structalias.c (callused_id): Remove.
>        (call_stmt_vars): New.
>        (get_call_vi): Likewise.
>        (lookup_call_use_vi): Likewise.
>        (lookup_call_clobber_vi): Likewise.
>        (get_call_use_vi): Likewise.
>        (get_call_clobber_vi): Likewise.
>        (make_transitive_closure_constraints): Likewise.
>        (handle_const_call): Adjust to do per-call call-used handling.
>        (handle_pure_call): Likewise.
>        (find_what_var_points_to): Remove general callused handling.
>        (init_base_vars): Likewise.
>        (init_alias_vars): Initialize call_stmt_vars.
>        (compute_points_to_sets): Process call-used and call-clobbered
>        vars for call statements.
>        (delete_points_to_sets): Free call_stmt_vars.
>
> Index: trunk/gcc/tree-ssa-structalias.c
> ===================================================================
> *** trunk.orig/gcc/tree-ssa-structalias.c       2009-11-19 13:55:23.000000000 +0100
> --- trunk/gcc/tree-ssa-structalias.c    2009-11-19 16:40:09.000000000 +0100
> *************** get_varinfo (unsigned int n)
> *** 287,294 ****
>
>  /* 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,
> !        storedanything_id = 6, integer_id = 7 };
>
>  struct GTY(()) heapvar_map {
>    struct tree_map map;
> --- 287,294 ----
>
>  /* Static IDs for the special variables.  */
>  enum { nothing_id = 0, anything_id = 1, readonly_id = 2,
> !        escaped_id = 3, nonlocal_id = 4,
> !        storedanything_id = 5, integer_id = 6 };
>
>  struct GTY(()) heapvar_map {
>    struct tree_map map;
> *************** new_var_info (tree t, const char *name)
> *** 378,383 ****
> --- 378,463 ----
>    return ret;
>  }
>
> +
> + /* A map mapping call statements to per-stmt variables for uses
> +    and clobbers specific to the call.  */
> + struct pointer_map_t *call_stmt_vars;
> +
> + /* Lookup or create the variable for the call statement CALL.  */
> +
> + static varinfo_t
> + get_call_vi (gimple call)
> + {
> +   void **slot_p;
> +   varinfo_t vi, vi2;
> +
> +   slot_p = pointer_map_insert (call_stmt_vars, call);
> +   if (*slot_p)
> +     return (varinfo_t) *slot_p;
> +
> +   vi = new_var_info (NULL_TREE, "CALLUSED");
> +   vi->offset = 0;
> +   vi->size = 1;
> +   vi->fullsize = 2;
> +   vi->is_full_var = true;
> +
> +   vi->next = vi2 = new_var_info (NULL_TREE, "CALLCLOBBERED");
> +   vi2->offset = 1;
> +   vi2->size = 1;
> +   vi2->fullsize = 2;
> +   vi2->is_full_var = true;
> +
> +   *slot_p = (void *) vi;
> +   return vi;
> + }
> +
> + /* Lookup the variable for the call statement CALL representing
> +    the uses.  Returns NULL if there is nothing special about this call.  */
> +
> + static varinfo_t
> + lookup_call_use_vi (gimple call)
> + {
> +   void **slot_p;
> +
> +   slot_p = pointer_map_contains (call_stmt_vars, call);
> +   if (slot_p)
> +     return (varinfo_t) *slot_p;
> +
> +   return NULL;
> + }
> +
> + /* Lookup the variable for the call statement CALL representing
> +    the clobbers.  Returns NULL if there is nothing special about this call.  */
> +
> + static varinfo_t
> + lookup_call_clobber_vi (gimple call)
> + {
> +   varinfo_t uses = lookup_call_use_vi (call);
> +   if (!uses)
> +     return NULL;
> +
> +   return uses->next;
> + }
> +
> + /* Lookup or create the variable for the call statement CALL representing
> +    the uses.  */
> +
> + static varinfo_t
> + get_call_use_vi (gimple call)
> + {
> +   return get_call_vi (call);
> + }
> +
> + /* Lookup or create the variable for the call statement CALL representing
> +    the clobbers.  */
> +
> + static varinfo_t ATTRIBUTE_UNUSED
> + get_call_clobber_vi (gimple call)
> + {
> +   return get_call_vi (call)->next;
> + }
> +
> +
>  typedef enum {SCALAR, DEREF, ADDRESSOF} constraint_expr_type;
>
>  /* An expression that appears in a constraint.  */
> *************** make_escape_constraint (tree op)
> *** 3381,3386 ****
> --- 3461,3492 ----
>    make_constraint_to (escaped_id, op);
>  }
>
> + /* Add constraints to that the solution of VI is transitively closed.  */
> +
> + static void
> + make_transitive_closure_constraints (varinfo_t vi)
> + {
> +   struct constraint_expr lhs, rhs;
> +
> +   /* VAR = *VAR;  */
> +   lhs.type = SCALAR;
> +   lhs.var = vi->id;
> +   lhs.offset = 0;
> +   rhs.type = DEREF;
> +   rhs.var = vi->id;
> +   rhs.offset = 0;
> +   process_constraint (new_constraint (lhs, rhs));
> +
> +   /* VAR = VAR + UNKNOWN;  */
> +   lhs.type = SCALAR;
> +   lhs.var = vi->id;
> +   lhs.offset = 0;
> +   rhs.type = SCALAR;
> +   rhs.var = vi->id;
> +   rhs.offset = UNKNOWN_OFFSET;
> +   process_constraint (new_constraint (lhs, rhs));
> + }
> +
>  /* Create a new artificial heap variable with NAME and make a
>     constraint from it to LHS.  Return the created variable.  */
>
> *************** handle_const_call (gimple stmt, VEC(ce_s
> *** 3536,3543 ****
>       as the static chain is concerned.  */
>    if (gimple_call_chain (stmt))
>      {
> !       make_constraint_to (callused_id, gimple_call_chain (stmt));
> !       rhsc.var = callused_id;
>        rhsc.offset = 0;
>        rhsc.type = SCALAR;
>        VEC_safe_push (ce_s, heap, *results, &rhsc);
> --- 3642,3651 ----
>       as the static chain is concerned.  */
>    if (gimple_call_chain (stmt))
>      {
> !       varinfo_t uses = get_call_use_vi (stmt);
> !       make_transitive_closure_constraints (uses);
> !       make_constraint_to (uses->id, gimple_call_chain (stmt));
> !       rhsc.var = uses->id;
>        rhsc.offset = 0;
>        rhsc.type = SCALAR;
>        VEC_safe_push (ce_s, heap, *results, &rhsc);
> *************** handle_pure_call (gimple stmt, VEC(ce_s,
> *** 3575,3581 ****
>  {
>    struct constraint_expr rhsc;
>    unsigned i;
> !   bool need_callused = false;
>
>    /* Memory reached from pointer arguments is call-used.  */
>    for (i = 0; i < gimple_call_num_args (stmt); ++i)
> --- 3683,3689 ----
>  {
>    struct constraint_expr rhsc;
>    unsigned i;
> !   varinfo_t uses = NULL;
>
>    /* Memory reached from pointer arguments is call-used.  */
>    for (i = 0; i < gimple_call_num_args (stmt); ++i)
> *************** handle_pure_call (gimple stmt, VEC(ce_s,
> *** 3584,3605 ****
>
>        if (could_have_pointers (arg))
>        {
> !         make_constraint_to (callused_id, arg);
> !         need_callused = true;
>        }
>      }
>
>    /* The static chain is used as well.  */
>    if (gimple_call_chain (stmt))
>      {
> !       make_constraint_to (callused_id, gimple_call_chain (stmt));
> !       need_callused = true;
>      }
>
> !   /* Pure functions may return callused and nonlocal memory.  */
> !   if (need_callused)
>      {
> !       rhsc.var = callused_id;
>        rhsc.offset = 0;
>        rhsc.type = SCALAR;
>        VEC_safe_push (ce_s, heap, *results, &rhsc);
> --- 3692,3721 ----
>
>        if (could_have_pointers (arg))
>        {
> !         if (!uses)
> !           {
> !             uses = get_call_use_vi (stmt);
> !             make_transitive_closure_constraints (uses);
> !           }
> !         make_constraint_to (uses->id, arg);
>        }
>      }
>
>    /* The static chain is used as well.  */
>    if (gimple_call_chain (stmt))
>      {
> !       if (!uses)
> !       {
> !         uses = get_call_use_vi (stmt);
> !         make_transitive_closure_constraints (uses);
> !       }
> !       make_constraint_to (uses->id, gimple_call_chain (stmt));
>      }
>
> !   /* Pure functions may return call-used and nonlocal memory.  */
> !   if (uses)
>      {
> !       rhsc.var = uses->id;
>        rhsc.offset = 0;
>        rhsc.type = SCALAR;
>        VEC_safe_push (ce_s, heap, *results, &rhsc);
> *************** find_func_aliases (gimple origt)
> *** 4004,4011 ****
>          if (!allows_reg && allows_mem)
>            make_escape_constraint (build_fold_addr_expr (op));
>          /* Strictly we'd only need the constraint to ESCAPED if
> !            the asm clobbers memory, otherwise using CALLUSED
> !            would be enough.  */
>          else if (op && could_have_pointers (op))
>            make_escape_constraint (op);
>        }
> --- 4120,4127 ----
>          if (!allows_reg && allows_mem)
>            make_escape_constraint (build_fold_addr_expr (op));
>          /* Strictly we'd only need the constraint to ESCAPED if
> !            the asm clobbers memory, otherwise using something
> !            along the lines of per-call clobbers/uses would be enough.  */
>          else if (op && could_have_pointers (op))
>            make_escape_constraint (op);
>        }
> *************** find_what_var_points_to (varinfo_t vi, s
> *** 4809,4816 ****
>            pt->null = 1;
>          else if (vi->id == escaped_id)
>            pt->escaped = 1;
> -         else if (vi->id == callused_id)
> -           gcc_unreachable ();
>          else if (vi->id == nonlocal_id)
>            pt->nonlocal = 1;
>          else if (vi->is_heap_var)
> --- 4925,4930 ----
> *************** init_base_vars (void)
> *** 5136,5142 ****
>    varinfo_t var_readonly;
>    varinfo_t var_escaped;
>    varinfo_t var_nonlocal;
> -   varinfo_t var_callused;
>    varinfo_t var_storedanything;
>    varinfo_t var_integer;
>
> --- 5250,5255 ----
> *************** init_base_vars (void)
> *** 5263,5297 ****
>    rhs.offset = 0;
>    process_constraint (new_constraint (lhs, rhs));
>
> -   /* Create the CALLUSED variable, used to represent the set of call-used
> -      memory.  */
> -   var_callused = new_var_info (NULL_TREE, "CALLUSED");
> -   gcc_assert (var_callused->id == callused_id);
> -   var_callused->is_artificial_var = 1;
> -   var_callused->offset = 0;
> -   var_callused->size = ~0;
> -   var_callused->fullsize = ~0;
> -   var_callused->is_special_var = 0;
> -
> -   /* 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 (new_constraint (lhs, rhs));
> -
> -   /* CALLUSED = CALLUSED + UNKNOWN, because if a sub-field is call-used the
> -      whole variable is call-used.  */
> -   lhs.type = SCALAR;
> -   lhs.var = callused_id;
> -   lhs.offset = 0;
> -   rhs.type = SCALAR;
> -   rhs.var = callused_id;
> -   rhs.offset = UNKNOWN_OFFSET;
> -   process_constraint (new_constraint (lhs, rhs));
> -
>    /* Create the STOREDANYTHING variable, used to represent the set of
>       variables stored to *ANYTHING.  */
>    var_storedanything = new_var_info (NULL_TREE, "STOREDANYTHING");
> --- 5376,5381 ----
> *************** init_alias_vars (void)
> *** 5342,5347 ****
> --- 5426,5432 ----
>    constraints = VEC_alloc (constraint_t, heap, 8);
>    varmap = VEC_alloc (varinfo_t, heap, 8);
>    vi_for_tree = pointer_map_create ();
> +   call_stmt_vars = pointer_map_create ();
>
>    memset (&stats, 0, sizeof (stats));
>    shared_bitmap_table = htab_create (511, shared_bitmap_hash,
> *************** compute_points_to_sets (void)
> *** 5478,5484 ****
>    basic_block bb;
>    unsigned i;
>    varinfo_t vi;
> -   struct pt_solution callused;
>
>    timevar_push (TV_TREE_PTA);
>
> --- 5563,5568 ----
> *************** compute_points_to_sets (void)
> *** 5511,5521 ****
>    /* From the constraints compute the points-to sets.  */
>    solve_constraints ();
>
> !   /* Compute the points-to sets for ESCAPED and CALLUSED used for
> !      call-clobber analysis.  */
>    find_what_var_points_to (get_varinfo (escaped_id),
>                           &cfun->gimple_df->escaped);
> -   find_what_var_points_to (get_varinfo (callused_id), &callused);
>
>    /* Make sure the ESCAPED solution (which is used as placeholder in
>       other solutions) does not reference itself.  This simplifies
> --- 5595,5603 ----
>    /* From the constraints compute the points-to sets.  */
>    solve_constraints ();
>
> !   /* Compute the points-to set for ESCAPED used for call-clobber analysis.  */
>    find_what_var_points_to (get_varinfo (escaped_id),
>                           &cfun->gimple_df->escaped);
>
>    /* Make sure the ESCAPED solution (which is used as placeholder in
>       other solutions) does not reference itself.  This simplifies
> *************** compute_points_to_sets (void)
> *** 5554,5564 ****
>          pt = gimple_call_use_set (stmt);
>          if (gimple_call_flags (stmt) & ECF_CONST)
>            memset (pt, 0, sizeof (struct pt_solution));
> !         else if (gimple_call_flags (stmt) & ECF_PURE)
>            {
> !             /* For const calls we should now be able to compute the
> !                call-used set per function.  */
> !             *pt = callused;
>              /* ???  ESCAPED can be empty even though NONLOCAL
>                 always escaped.  */
>              pt->nonlocal = 1;
> --- 5636,5646 ----
>          pt = gimple_call_use_set (stmt);
>          if (gimple_call_flags (stmt) & ECF_CONST)
>            memset (pt, 0, sizeof (struct pt_solution));
> !         else if ((vi = lookup_call_use_vi (stmt)) != NULL)
>            {
> !             find_what_var_points_to (vi, pt);
> !             /* Escaped (and thus nonlocal) variables are always
> !                implicitly used by calls.  */
>              /* ???  ESCAPED can be empty even though NONLOCAL
>                 always escaped.  */
>              pt->nonlocal = 1;
> *************** compute_points_to_sets (void)
> *** 5566,5571 ****
> --- 5648,5655 ----
>            }
>          else
>            {
> +             /* If there is nothing special about this call then
> +                we have made everything that is used also escape.  */
>              *pt = cfun->gimple_df->escaped;
>              pt->nonlocal = 1;
>            }
> *************** compute_points_to_sets (void)
> *** 5573,5580 ****
> --- 5657,5676 ----
>          pt = gimple_call_clobber_set (stmt);
>          if (gimple_call_flags (stmt) & (ECF_CONST|ECF_PURE|ECF_NOVOPS))
>            memset (pt, 0, sizeof (struct pt_solution));
> +         else if ((vi = lookup_call_clobber_vi (stmt)) != NULL)
> +           {
> +             find_what_var_points_to (vi, pt);
> +             /* Escaped (and thus nonlocal) variables are always
> +                implicitly clobbered by calls.  */
> +             /* ???  ESCAPED can be empty even though NONLOCAL
> +                always escaped.  */
> +             pt->nonlocal = 1;
> +             pt->escaped = 1;
> +           }
>          else
>            {
> +             /* If there is nothing special about this call then
> +                we have made everything that is used also escape.  */
>              *pt = cfun->gimple_df->escaped;
>              pt->nonlocal = 1;
>            }
> *************** delete_points_to_sets (void)
> *** 5598,5603 ****
> --- 5694,5700 ----
>             stats.points_to_sets_created);
>
>    pointer_map_destroy (vi_for_tree);
> +   pointer_map_destroy (call_stmt_vars);
>    bitmap_obstack_release (&pta_obstack);
>    VEC_free (constraint_t, heap, constraints);
>
>



More information about the Gcc-patches mailing list