/* 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 };
+ escaped_id = 3, nonlocal_id = 4,
+ storedanything_id = 5, integer_id = 6 };
struct GTY(()) heapvar_map {
struct tree_map map;
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_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. */
as the static chain is concerned. */
if (gimple_call_chain (stmt))
{
- make_constraint_to (callused_id, gimple_call_chain (stmt));
- rhsc.var = callused_id;
+ 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);
{
struct constraint_expr rhsc;
unsigned i;
- bool need_callused = false;
+ varinfo_t uses = NULL;
/* Memory reached from pointer arguments is call-used. */
for (i = 0; i < gimple_call_num_args (stmt); ++i)
if (could_have_pointers (arg))
{
- make_constraint_to (callused_id, arg);
- need_callused = true;
+ 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))
{
- make_constraint_to (callused_id, gimple_call_chain (stmt));
- need_callused = true;
+ 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 callused and nonlocal memory. */
- if (need_callused)
+ /* Pure functions may return call-used and nonlocal memory. */
+ if (uses)
{
- rhsc.var = callused_id;
+ rhsc.var = uses->id;
rhsc.offset = 0;
rhsc.type = SCALAR;
VEC_safe_push (ce_s, heap, *results, &rhsc);
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. */
+ 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);
}
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)
varinfo_t var_readonly;
varinfo_t var_escaped;
varinfo_t var_nonlocal;
- varinfo_t var_callused;
varinfo_t var_storedanything;
varinfo_t var_integer;
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");
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,
basic_block bb;
unsigned i;
varinfo_t vi;
- struct pt_solution callused;
timevar_push (TV_TREE_PTA);
/* From the constraints compute the points-to sets. */
solve_constraints ();
- /* Compute the points-to sets for ESCAPED and CALLUSED used for
- call-clobber analysis. */
+ /* 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);
- 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
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)
+ else if ((vi = lookup_call_use_vi (stmt)) != NULL)
{
- /* For const calls we should now be able to compute the
- call-used set per function. */
- *pt = callused;
+ 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;
}
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;
}
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;
}
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);