This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH][RFC] Make pure/const call clobber analysis context-sensitive
- From: Richard Guenther <rguenther at suse dot de>
- To: gcc-patches at gcc dot gnu dot org
- Date: Thu, 19 Nov 2009 17:10:33 +0100 (CET)
- Subject: [PATCH][RFC] Make pure/const call clobber analysis context-sensitive
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.
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);