[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