#include "hard-reg-set.h"
#include "basic-block.h"
#include "output.h"
-#include "errors.h"
-#include "diagnostic.h"
#include "tree.h"
#include "c-common.h"
#include "tree-flow.h"
#include "tree-inline.h"
#include "varray.h"
#include "c-tree.h"
-#include "tree-gimple.h"
+#include "diagnostic.h"
+#include "toplev.h"
+#include "gimple.h"
#include "hashtab.h"
#include "function.h"
#include "cgraph.h"
/* ID of this variable */
unsigned int id;
- /* Name of this variable */
- const char *name;
-
- /* Tree that this variable is associated with. */
- tree decl;
-
- /* Offset of this variable, in bits, from the base variable */
- unsigned HOST_WIDE_INT offset;
-
- /* Size of the variable, in bits. */
- unsigned HOST_WIDE_INT size;
-
- /* Full size of the base variable, in bits. */
- unsigned HOST_WIDE_INT fullsize;
-
- /* A link to the variable for the next field in this structure. */
- struct variable_info *next;
-
/* True if this is a variable created by the constraint analysis, such as
heap variables and constraints we had to break up. */
unsigned int is_artificial_var:1;
/* True for variables whose size is not known or variable. */
unsigned int is_unknown_size_var:1;
- /* True for variables that have unions somewhere in them. */
- unsigned int has_union:1;
+ /* True for (sub-)fields that represent a whole variable. */
+ unsigned int is_full_var : 1;
/* True if this is a heap variable. */
unsigned int is_heap_var:1;
variable. This is used for C++ placement new. */
unsigned int no_tbaa_pruning : 1;
+ /* Variable id this was collapsed to due to type unsafety. Zero if
+ this variable was not collapsed. This should be unused completely
+ after build_succ_graph, or something is broken. */
+ unsigned int collapsed_to;
+
+ /* A link to the variable for the next field in this structure. */
+ struct variable_info *next;
+
+ /* Offset of this variable, in bits, from the base variable */
+ unsigned HOST_WIDE_INT offset;
+
+ /* Size of the variable, in bits. */
+ unsigned HOST_WIDE_INT size;
+
+ /* Full size of the base variable, in bits. */
+ unsigned HOST_WIDE_INT fullsize;
+
+ /* Name of this variable */
+ const char *name;
+
+ /* Tree that this variable is associated with. */
+ tree decl;
+
/* Points-to set for this variable. */
bitmap solution;
/* Old points-to set for this variable. */
bitmap oldsolution;
-
- /* Variable id this was collapsed to due to type unsafety. This
- should be unused completely after build_succ_graph, or something
- is broken. */
- struct variable_info *collapsed_to;
};
typedef struct variable_info *varinfo_t;
static varinfo_t first_vi_for_offset (varinfo_t, unsigned HOST_WIDE_INT);
+static varinfo_t lookup_vi_for_tree (tree);
/* Pool of variable info structures. */
static alloc_pool variable_info_pool;
{
varinfo_t v = VEC_index (varinfo_t, varmap, n);
- if (v->collapsed_to)
- return v->collapsed_to;
+ if (v->collapsed_to != 0)
+ return get_varinfo (v->collapsed_to);
return v;
}
ret->is_heap_var = false;
ret->is_special_var = false;
ret->is_unknown_size_var = false;
- ret->has_union = false;
+ ret->is_full_var = false;
var = t;
if (TREE_CODE (var) == SSA_NAME)
var = SSA_NAME_VAR (var);
ret->solution = BITMAP_ALLOC (&pta_obstack);
ret->oldsolution = BITMAP_ALLOC (&oldpta_obstack);
ret->next = NULL;
- ret->collapsed_to = NULL;
+ ret->collapsed_to = 0;
return ret;
}
taken. Used for variable substitution. */
bitmap address_taken;
- /* True if points_to bitmap for this node is stored in the hash
- table. */
- sbitmap pt_used;
-
- /* Number of incoming edges remaining to be processed by pointer
- equivalence.
- Used for variable substitution. */
- unsigned int *number_incoming;
-
-
/* Vector of complex constraints for each graph node. Complex
constraints are those involving dereferences or offsets that are
not 0. */
dump_constraints (stderr);
}
+/* Print out to FILE the edge in the constraint graph that is created by
+ constraint c. The edge may have a label, depending on the type of
+ constraint that it represents. If complex1, e.g: a = *b, then the label
+ is "=*", if complex2, e.g: *a = b, then the label is "*=", if
+ complex with an offset, e.g: a = b + 8, then the label is "+".
+ Otherwise the edge has no label. */
+
+void
+dump_constraint_edge (FILE *file, constraint_t c)
+{
+ if (c->rhs.type != ADDRESSOF)
+ {
+ const char *src = get_varinfo_fc (c->rhs.var)->name;
+ const char *dst = get_varinfo_fc (c->lhs.var)->name;
+ fprintf (file, " \"%s\" -> \"%s\" ", src, dst);
+ /* Due to preprocessing of constraints, instructions like *a = *b are
+ illegal; thus, we do not have to handle such cases. */
+ if (c->lhs.type == DEREF)
+ fprintf (file, " [ label=\"*=\" ] ;\n");
+ else if (c->rhs.type == DEREF)
+ fprintf (file, " [ label=\"=*\" ] ;\n");
+ else
+ {
+ /* We must check the case where the constraint is an offset.
+ In this case, it is treated as a complex constraint. */
+ if (c->rhs.offset != c->lhs.offset)
+ fprintf (file, " [ label=\"+\" ] ;\n");
+ else
+ fprintf (file, " ;\n");
+ }
+ }
+}
+
+/* Print the constraint graph in dot format. */
+
+void
+dump_constraint_graph (FILE *file)
+{
+ unsigned int i=0, size;
+ constraint_t c;
+
+ /* Only print the graph if it has already been initialized: */
+ if (!graph)
+ return;
+
+ /* Print the constraints used to produce the constraint graph. The
+ constraints will be printed as comments in the dot file: */
+ fprintf (file, "\n\n/* Constraints used in the constraint graph:\n");
+ dump_constraints (file);
+ fprintf (file, "*/\n");
+
+ /* Prints the header of the dot file: */
+ fprintf (file, "\n\n// The constraint graph in dot format:\n");
+ fprintf (file, "strict digraph {\n");
+ fprintf (file, " node [\n shape = box\n ]\n");
+ fprintf (file, " edge [\n fontsize = \"12\"\n ]\n");
+ fprintf (file, "\n // List of nodes in the constraint graph:\n");
+
+ /* The next lines print the nodes in the graph. In order to get the
+ number of nodes in the graph, we must choose the minimum between the
+ vector VEC (varinfo_t, varmap) and graph->size. If the graph has not
+ yet been initialized, then graph->size == 0, otherwise we must only
+ read nodes that have an entry in VEC (varinfo_t, varmap). */
+ size = VEC_length (varinfo_t, varmap);
+ size = size < graph->size ? size : graph->size;
+ for (i = 0; i < size; i++)
+ {
+ const char *name = get_varinfo_fc (graph->rep[i])->name;
+ fprintf (file, " \"%s\" ;\n", name);
+ }
+
+ /* Go over the list of constraints printing the edges in the constraint
+ graph. */
+ fprintf (file, "\n // The constraint edges:\n");
+ for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
+ if (c)
+ dump_constraint_edge (file, c);
+
+ /* Prints the tail of the dot file. By now, only the closing bracket. */
+ fprintf (file, "}\n\n\n");
+}
+
+/* Print out the constraint graph to stderr. */
+
+void
+debug_constraint_graph (void)
+{
+ dump_constraint_graph (stderr);
+}
+
/* SOLVER FUNCTIONS
The solver is a simple worklist solver, that works on the following
EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
{
- /* If this is a properly sized variable, only add offset if it's
- less than end. Otherwise, it is globbed to a single
- variable. */
+ varinfo_t vi = get_varinfo (i);
- if ((get_varinfo (i)->offset + offset) < get_varinfo (i)->fullsize)
+ /* If this is a variable with just one field just set its bit
+ in the result. */
+ if (vi->is_artificial_var
+ || vi->is_unknown_size_var
+ || vi->is_full_var)
+ bitmap_set_bit (result, i);
+ else
{
- unsigned HOST_WIDE_INT fieldoffset = get_varinfo (i)->offset + offset;
- varinfo_t v = first_vi_for_offset (get_varinfo (i), fieldoffset);
+ unsigned HOST_WIDE_INT fieldoffset = vi->offset + offset;
+ varinfo_t v = first_vi_for_offset (vi, fieldoffset);
+ /* If the result is outside of the variable use the last field. */
if (!v)
- continue;
+ {
+ v = vi;
+ while (v->next != NULL)
+ v = v->next;
+ }
bitmap_set_bit (result, v->id);
- }
- else if (get_varinfo (i)->is_artificial_var
- || get_varinfo (i)->has_union
- || get_varinfo (i)->is_unknown_size_var)
- {
- bitmap_set_bit (result, i);
+ /* If the result is not exactly at fieldoffset include the next
+ field as well. See get_constraint_for_ptr_offset for more
+ rationale. */
+ if (v->offset != fieldoffset
+ && v->next != NULL)
+ bitmap_set_bit (result, v->next->id);
}
}
if (!graph->implicit_preds[to])
graph->implicit_preds[to] = BITMAP_ALLOC (&predbitmap_obstack);
- if (!bitmap_bit_p (graph->implicit_preds[to], from))
- {
- stats.num_implicit_edges++;
- bitmap_set_bit (graph->implicit_preds[to], from);
- }
+ if (bitmap_set_bit (graph->implicit_preds[to], from))
+ stats.num_implicit_edges++;
}
/* Add a predecessor graph edge to GRAPH, going from TO to FROM if
{
if (!graph->preds[to])
graph->preds[to] = BITMAP_ALLOC (&predbitmap_obstack);
- if (!bitmap_bit_p (graph->preds[to], from))
- bitmap_set_bit (graph->preds[to], from);
+ bitmap_set_bit (graph->preds[to], from);
}
/* Add a graph edge to GRAPH, going from FROM to TO if
if (!graph->succs[from])
graph->succs[from] = BITMAP_ALLOC (&pta_obstack);
- if (!bitmap_bit_p (graph->succs[from], to))
+ if (bitmap_set_bit (graph->succs[from], to))
{
r = true;
if (to < FIRST_REF_NODE && from < FIRST_REF_NODE)
stats.num_edges++;
- bitmap_set_bit (graph->succs[from], to);
}
return r;
}
graph->points_to = XCNEWVEC (bitmap, graph->size);
graph->eq_rep = XNEWVEC (int, graph->size);
graph->direct_nodes = sbitmap_alloc (graph->size);
- graph->pt_used = sbitmap_alloc (graph->size);
graph->address_taken = BITMAP_ALLOC (&predbitmap_obstack);
- graph->number_incoming = XCNEWVEC (unsigned int, graph->size);
sbitmap_zero (graph->direct_nodes);
- sbitmap_zero (graph->pt_used);
for (j = 0; j < FIRST_REF_NODE; j++)
{
}
else if (rhs.type == ADDRESSOF)
{
+ varinfo_t v;
+
/* x = &y */
if (graph->points_to[lhsvar] == NULL)
graph->points_to[lhsvar] = BITMAP_ALLOC (&predbitmap_obstack);
/* Implicitly, *x = y */
add_implicit_graph_edge (graph, FIRST_REF_NODE + lhsvar, rhsvar);
+ /* All related variables are no longer direct nodes. */
RESET_BIT (graph->direct_nodes, rhsvar);
+ v = get_varinfo (rhsvar);
+ if (!v->is_full_var)
+ {
+ v = lookup_vi_for_tree (v->decl);
+ do
+ {
+ RESET_BIT (graph->direct_nodes, v->id);
+ v = v->next;
+ }
+ while (v != NULL);
+ }
bitmap_set_bit (graph->address_taken, rhsvar);
}
else if (lhsvar > anything_id
0. */
if (ninfo->is_special_var
|| ninfo->is_artificial_var
- || ninfo->is_unknown_size_var)
+ || ninfo->is_unknown_size_var
+ || ninfo->is_full_var)
{
*offset = 0;
return true;
unsigned int j;
bitmap_iterator bi;
- if (bitmap_bit_p (delta, anything_id))
- {
- flag = !bitmap_bit_p (sol, anything_id);
- if (flag)
- bitmap_set_bit (sol, anything_id);
- goto done;
- }
+ /* For x = *ESCAPED and x = *CALLUSED we want to compute the
+ reachability set of the rhs var. As a pointer to a sub-field
+ of a variable can also reach all other fields of the variable
+ we simply have to expand the solution to contain all sub-fields
+ if one sub-field is contained. */
+ if (c->rhs.var == escaped_id
+ || c->rhs.var == callused_id)
+ {
+ bitmap vars = NULL;
+ /* In a first pass record all variables we need to add all
+ sub-fields off. This avoids quadratic behavior. */
+ EXECUTE_IF_SET_IN_BITMAP (delta, 0, j, bi)
+ {
+ varinfo_t v = get_varinfo (j);
+ if (v->is_full_var)
+ continue;
+
+ v = lookup_vi_for_tree (v->decl);
+ if (v->next != NULL)
+ {
+ if (vars == NULL)
+ vars = BITMAP_ALLOC (NULL);
+ bitmap_set_bit (vars, v->id);
+ }
+ }
+ /* In the second pass now do the addition to the solution and
+ to speed up solving add it to the delta as well. */
+ if (vars != NULL)
+ {
+ EXECUTE_IF_SET_IN_BITMAP (vars, 0, j, bi)
+ {
+ varinfo_t v = get_varinfo (j);
+ for (; v != NULL; v = v->next)
+ {
+ if (bitmap_set_bit (sol, v->id))
+ {
+ flag = true;
+ bitmap_set_bit (delta, v->id);
+ }
+ }
+ }
+ BITMAP_FREE (vars);
+ }
+ }
+
+ if (bitmap_bit_p (delta, anything_id))
+ {
+ flag |= bitmap_set_bit (sol, anything_id);
+ goto done;
+ }
/* For each variable j in delta (Sol(y)), add
an edge in the graph from j to x, and union Sol(j) into Sol(x). */
unsigned int t;
v = first_vi_for_offset (get_varinfo (j), fieldoffset);
+ /* If the access is outside of the variable we can ignore it. */
if (!v)
continue;
t = find (v->id);
/* Merging the solution from ESCAPED needlessly increases
the set. Use ESCAPED as representative instead.
Same for CALLUSED. */
- else if ((get_varinfo (t)->id == escaped_id
- || get_varinfo (t)->id == callused_id)
- && !bitmap_bit_p (sol, get_varinfo (t)->id))
- {
- bitmap_set_bit (sol, get_varinfo (t)->id);
- flag = true;
- }
+ else if (get_varinfo (t)->id == escaped_id
+ || get_varinfo (t)->id == callused_id)
+ flag |= bitmap_set_bit (sol, get_varinfo (t)->id);
else if (add_graph_edge (graph, lhs, t))
flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
}
unsigned HOST_WIDE_INT fieldoffset = jvi->offset + loff;
varinfo_t v;
- v = first_vi_for_offset (get_varinfo (j), fieldoffset);
- if (!v)
- continue;
+ v = get_varinfo (j);
+ if (!v->is_full_var)
+ {
+ v = first_vi_for_offset (v, fieldoffset);
+ /* If the access is outside of the variable we can ignore it. */
+ if (!v)
+ continue;
+ }
t = find (v->id);
- if (!bitmap_bit_p (get_varinfo (t)->solution, anything_id))
+ if (bitmap_set_bit (get_varinfo (t)->solution, anything_id)
+ && !TEST_BIT (changed, t))
{
- bitmap_set_bit (get_varinfo (t)->solution, anything_id);
- if (!TEST_BIT (changed, t))
- {
- SET_BIT (changed, t);
- changed_count++;
- }
+ SET_BIT (changed, t);
+ changed_count++;
}
}
return;
bitmap tmp;
v = first_vi_for_offset (get_varinfo (j), fieldoffset);
+ /* If the access is outside of the variable we can ignore it. */
if (!v)
continue;
t = find (v->id);
tmp = get_varinfo (t)->solution;
- if (set_union_with_increment (tmp, sol, 0))
+ if (add_graph_edge (graph, t, rhs))
{
- get_varinfo (t)->solution = tmp;
- if (t == rhs)
- sol = get_varinfo (rhs)->solution;
- if (!TEST_BIT (changed, t))
+ if (bitmap_ior_into (get_varinfo (t)->solution, sol))
{
- SET_BIT (changed, t);
- changed_count++;
+ if (t == rhs)
+ sol = get_varinfo (rhs)->solution;
+ if (!TEST_BIT (changed, t))
+ {
+ SET_BIT (changed, t);
+ changed_count++;
+ }
}
}
}
bitmap_ior_into (graph->points_to[n],
graph->points_to[w]);
}
- EXECUTE_IF_IN_NONNULL_BITMAP (graph->preds[n], 0, i, bi)
- {
- unsigned int rep = si->node_mapping[i];
- graph->number_incoming[rep]++;
- }
}
SET_BIT (si->deleted, n);
}
/* Skip unused edges */
if (w == n || graph->pointer_label[w] == 0)
- {
- graph->number_incoming[w]--;
- continue;
- }
+ continue;
+
if (graph->points_to[w])
bitmap_ior_into(graph->points_to[n], graph->points_to[w]);
-
- /* If all incoming edges to w have been processed and
- graph->points_to[w] was not stored in the hash table, we can
- free it. */
- graph->number_incoming[w]--;
- if (!graph->number_incoming[w] && !TEST_BIT (graph->pt_used, w))
- {
- BITMAP_FREE (graph->points_to[w]);
- }
}
/* Indirect nodes get fresh variables. */
if (!TEST_BIT (graph->direct_nodes, n))
graph->points_to[n]);
if (!label)
{
- SET_BIT (graph->pt_used, n);
label = pointer_equiv_class++;
equiv_class_add (pointer_equiv_class_table,
label, graph->points_to[n]);
free (graph->loc_label);
free (graph->pointed_by);
free (graph->points_to);
- free (graph->number_incoming);
free (graph->eq_rep);
sbitmap_free (graph->direct_nodes);
- sbitmap_free (graph->pt_used);
htab_delete (pointer_equiv_class_table);
htab_delete (location_equiv_class_table);
bitmap_obstack_release (&iteration_obstack);
gcc_assert (rhs.var < VEC_length (varinfo_t, varmap));
gcc_assert (lhs.var < VEC_length (varinfo_t, varmap));
- if (!use_field_sensitive)
- {
- t->rhs.offset = 0;
- t->lhs.offset = 0;
- }
-
/* ANYTHING == ANYTHING is pointless. */
if (lhs.var == anything_id && rhs.var == anything_id)
return;
tree type = TREE_TYPE (t);
if (POINTER_TYPE_P (type)
- || AGGREGATE_TYPE_P (type)
- || TREE_CODE (type) == COMPLEX_TYPE)
+ || AGGREGATE_TYPE_P (type))
return true;
return false;
/* Return the position, in bits, of FIELD_DECL from the beginning of its
structure. */
-static unsigned HOST_WIDE_INT
+static HOST_WIDE_INT
bitpos_of_field (const tree fdecl)
{
- if (TREE_CODE (DECL_FIELD_OFFSET (fdecl)) != INTEGER_CST
- || TREE_CODE (DECL_FIELD_BIT_OFFSET (fdecl)) != INTEGER_CST)
+ if (!host_integerp (DECL_FIELD_OFFSET (fdecl), 0)
+ || !host_integerp (DECL_FIELD_BIT_OFFSET (fdecl), 0))
return -1;
- return (tree_low_cst (DECL_FIELD_OFFSET (fdecl), 1) * 8)
- + tree_low_cst (DECL_FIELD_BIT_OFFSET (fdecl), 1);
+ return (TREE_INT_CST_LOW (DECL_FIELD_OFFSET (fdecl)) * 8
+ + TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (fdecl)));
+}
+
+
+/* Get constraint expressions for offsetting PTR by OFFSET. Stores the
+ resulting constraint expressions in *RESULTS. */
+
+static void
+get_constraint_for_ptr_offset (tree ptr, tree offset,
+ VEC (ce_s, heap) **results)
+{
+ struct constraint_expr *c;
+ unsigned int j, n;
+ unsigned HOST_WIDE_INT rhsunitoffset, rhsoffset;
+
+ /* If we do not do field-sensitive PTA adding offsets to pointers
+ does not change the points-to solution. */
+ if (!use_field_sensitive)
+ {
+ get_constraint_for (ptr, results);
+ return;
+ }
+
+ /* If the offset is not a non-negative integer constant that fits
+ in a HOST_WIDE_INT, we have to fall back to a conservative
+ solution which includes all sub-fields of all pointed-to
+ variables of ptr.
+ ??? As we do not have the ability to express this, fall back
+ to anything. */
+ if (!host_integerp (offset, 1))
+ {
+ struct constraint_expr temp;
+ temp.var = anything_id;
+ temp.type = SCALAR;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &temp);
+ return;
+ }
+
+ /* Make sure the bit-offset also fits. */
+ rhsunitoffset = TREE_INT_CST_LOW (offset);
+ rhsoffset = rhsunitoffset * BITS_PER_UNIT;
+ if (rhsunitoffset != rhsoffset / BITS_PER_UNIT)
+ {
+ struct constraint_expr temp;
+ temp.var = anything_id;
+ temp.type = SCALAR;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &temp);
+ return;
+ }
+
+ get_constraint_for (ptr, results);
+ if (rhsoffset == 0)
+ return;
+
+ /* As we are eventually appending to the solution do not use
+ VEC_iterate here. */
+ n = VEC_length (ce_s, *results);
+ for (j = 0; j < n; j++)
+ {
+ varinfo_t curr;
+ c = VEC_index (ce_s, *results, j);
+ curr = get_varinfo (c->var);
+
+ if (c->type == ADDRESSOF
+ && !curr->is_full_var)
+ {
+ varinfo_t temp, curr = get_varinfo (c->var);
+
+ /* Search the sub-field which overlaps with the
+ pointed-to offset. As we deal with positive offsets
+ only, we can start the search from the current variable. */
+ temp = first_vi_for_offset (curr, curr->offset + rhsoffset);
+
+ /* If the result is outside of the variable we have to provide
+ a conservative result, as the variable is still reachable
+ from the resulting pointer (even though it technically
+ cannot point to anything). The last sub-field is such
+ a conservative result.
+ ??? If we always had a sub-field for &object + 1 then
+ we could represent this in a more precise way. */
+ if (temp == NULL)
+ {
+ temp = curr;
+ while (temp->next != NULL)
+ temp = temp->next;
+ continue;
+ }
+
+ /* If the found variable is not exactly at the pointed to
+ result, we have to include the next variable in the
+ solution as well. Otherwise two increments by offset / 2
+ do not result in the same or a conservative superset
+ solution. */
+ if (temp->offset != curr->offset + rhsoffset
+ && temp->next != NULL)
+ {
+ struct constraint_expr c2;
+ c2.var = temp->next->id;
+ c2.type = ADDRESSOF;
+ c2.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &c2);
+ }
+ c->var = temp->id;
+ c->offset = 0;
+ }
+ else if (c->type == ADDRESSOF
+ /* If this varinfo represents a full variable just use it. */
+ && curr->is_full_var)
+ c->offset = 0;
+ else
+ c->offset = rhsoffset;
+ }
}
/* Pretend to take the address of the base, we'll take care of
adding the required subset of sub-fields below. */
get_constraint_for_1 (t, results, true);
- result = VEC_last (ce_s, *results);
-
gcc_assert (VEC_length (ce_s, *results) == 1);
+ result = VEC_last (ce_s, *results);
/* This can also happen due to weird offsetof type macros. */
if (TREE_CODE (t) != ADDR_EXPR && result->type == ADDRESSOF)
result->type = SCALAR;
- if (result->type == SCALAR)
+ if (result->type == SCALAR
+ && get_varinfo (result->var)->is_full_var)
+ /* For single-field vars do not bother about the offset. */
+ result->offset = 0;
+ else if (result->type == SCALAR)
{
/* In languages like C, you can access one past the end of an
array. You aren't allowed to dereference it, so we can
break;
}
}
- /* assert that we found *some* field there. The user couldn't be
- accessing *only* padding. */
- /* Still the user could access one past the end of an array
- embedded in a struct resulting in accessing *only* padding. */
- gcc_assert (VEC_length (ce_s, *results) >= 1
- || ref_contains_array_ref (orig_t));
+ /* If we are going to take the address of this field then
+ to be able to compute reachability correctly add at least
+ the last field of the variable. */
+ if (address_p
+ && VEC_length (ce_s, *results) == 0)
+ {
+ curr = get_varinfo (cexpr.var);
+ while (curr->next != NULL)
+ curr = curr->next;
+ cexpr.var = curr->id;
+ VEC_safe_push (ce_s, heap, *results, &cexpr);
+ }
+ else
+ /* Assert that we found *some* field there. The user couldn't be
+ accessing *only* padding. */
+ /* Still the user could access one past the end of an array
+ embedded in a struct resulting in accessing *only* padding. */
+ gcc_assert (VEC_length (ce_s, *results) >= 1
+ || ref_contains_array_ref (orig_t));
}
else if (bitmaxsize == 0)
{
happens below, since it will fall into the default case. The only
case we know something about an integer treated like a pointer is
when it is the NULL pointer, and then we just say it points to
- NULL. */
- if (TREE_CODE (t) == INTEGER_CST
+ NULL.
+
+ Do not do that if -fno-delete-null-pointer-checks though, because
+ in that case *NULL does not fail, so it _should_ alias *anything.
+ It is not worth adding a new option or renaming the existing one,
+ since this case is relatively obscure. */
+ if (flag_delete_null_pointer_checks
+ && TREE_CODE (t) == INTEGER_CST
&& integer_zerop (t))
{
temp.var = nothing_id;
switch (TREE_CODE_CLASS (TREE_CODE (t)))
{
case tcc_expression:
- case tcc_vl_exp:
{
switch (TREE_CODE (t))
{
return;
}
break;
- case CALL_EXPR:
- /* XXX: In interprocedural mode, if we didn't have the
- body, we would need to do *each pointer argument =
- &ANYTHING added. */
- if (call_expr_flags (t) & (ECF_MALLOC | ECF_MAY_BE_ALLOCA))
- {
- varinfo_t vi;
- tree heapvar = heapvar_lookup (t);
-
- if (heapvar == NULL)
- {
- heapvar = create_tmp_var_raw (ptr_type_node, "HEAP");
- DECL_EXTERNAL (heapvar) = 1;
- get_var_ann (heapvar)->is_heapvar = 1;
- if (gimple_referenced_vars (cfun))
- add_referenced_var (heapvar);
- heapvar_insert (t, heapvar);
- }
-
- temp.var = create_variable_info_for (heapvar,
- alias_get_name (heapvar));
-
- vi = get_varinfo (temp.var);
- vi->is_artificial_var = 1;
- vi->is_heap_var = 1;
- temp.type = ADDRESSOF;
- temp.offset = 0;
- VEC_safe_push (ce_s, heap, *results, &temp);
- return;
- }
- else
- {
- temp.var = anything_id;
- temp.type = SCALAR;
- temp.offset = 0;
- VEC_safe_push (ce_s, heap, *results, &temp);
- return;
- }
- break;
- default:
- {
- temp.type = ADDRESSOF;
- temp.var = anything_id;
- temp.offset = 0;
- VEC_safe_push (ce_s, heap, *results, &temp);
- return;
- }
+ default:;
}
+ break;
}
case tcc_reference:
{
case COMPONENT_REF:
get_constraint_for_component_ref (t, results, address_p);
return;
- default:
- {
- temp.type = ADDRESSOF;
- temp.var = anything_id;
- temp.offset = 0;
- VEC_safe_push (ce_s, heap, *results, &temp);
- return;
- }
- }
- }
- case tcc_unary:
- {
- switch (TREE_CODE (t))
- {
- CASE_CONVERT:
- {
- tree op = TREE_OPERAND (t, 0);
-
- /* Cast from non-pointer to pointers are bad news for us.
- Anything else, we see through */
- if (!(POINTER_TYPE_P (TREE_TYPE (t))
- && ! POINTER_TYPE_P (TREE_TYPE (op))))
- {
- get_constraint_for_1 (op, results, address_p);
- return;
- }
-
- /* FALLTHRU */
- }
- default:
- {
- temp.type = ADDRESSOF;
- temp.var = anything_id;
- temp.offset = 0;
- VEC_safe_push (ce_s, heap, *results, &temp);
- return;
- }
+ default:;
}
+ break;
}
case tcc_exceptional:
{
switch (TREE_CODE (t))
{
- case PHI_NODE:
- {
- get_constraint_for_1 (PHI_RESULT (t), results, address_p);
- return;
- }
- break;
case SSA_NAME:
{
get_constraint_for_ssa_var (t, results, address_p);
return;
}
- break;
- default:
- {
- temp.type = ADDRESSOF;
- temp.var = anything_id;
- temp.offset = 0;
- VEC_safe_push (ce_s, heap, *results, &temp);
- return;
- }
+ default:;
}
+ break;
}
case tcc_declaration:
{
get_constraint_for_ssa_var (t, results, address_p);
return;
}
- default:
- {
- temp.type = ADDRESSOF;
- temp.var = anything_id;
- temp.offset = 0;
- VEC_safe_push (ce_s, heap, *results, &temp);
- return;
- }
+ default:;
}
+
+ /* The default fallback is a constraint from anything. */
+ temp.type = ADDRESSOF;
+ temp.var = anything_id;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &temp);
}
/* Given a gimple tree T, return the constraint expression vector for it. */
fprintf (dump_file, "Type safety: Collapsing var %s into %s\n",
field->name, currvar->name);
- gcc_assert (!field->collapsed_to);
- field->collapsed_to = currvar;
+ gcc_assert (field->collapsed_to == 0);
+ field->collapsed_to = currvar->id;
}
currvar->next = NULL;
{
if (!do_simple_structure_copy (lhs, rhs, MIN (lhssize, rhssize)))
{
- lhs.var = collapse_rest_of_var (lhs.var);
- rhs.var = collapse_rest_of_var (rhs.var);
+ lhs.var = collapse_rest_of_var (get_varinfo_fc (lhs.var)->id);
+ rhs.var = collapse_rest_of_var (get_varinfo_fc (rhs.var)->id);
lhs.offset = 0;
rhs.offset = 0;
lhs.type = SCALAR;
}
}
-
-/* Handle pointer arithmetic EXPR when creating aliasing constraints.
- Expressions of the type PTR + CST can be handled in two ways:
-
- 1- If the constraint for PTR is ADDRESSOF for a non-structure
- variable, then we can use it directly because adding or
- subtracting a constant may not alter the original ADDRESSOF
- constraint (i.e., pointer arithmetic may not legally go outside
- an object's boundaries).
-
- 2- If the constraint for PTR is ADDRESSOF for a structure variable,
- then if CST is a compile-time constant that can be used as an
- offset, we can determine which sub-variable will be pointed-to
- by the expression.
-
- Return true if the expression is handled. For any other kind of
- expression, return false so that each operand can be added as a
- separate constraint by the caller. */
-
-static bool
-handle_ptr_arith (VEC (ce_s, heap) *lhsc, tree expr)
-{
- tree op0, op1;
- struct constraint_expr *c, *c2;
- unsigned int i = 0;
- unsigned int j = 0;
- VEC (ce_s, heap) *temp = NULL;
- unsigned HOST_WIDE_INT rhsunitoffset, rhsoffset;
-
- if (TREE_CODE (expr) != POINTER_PLUS_EXPR)
- return false;
-
- op0 = TREE_OPERAND (expr, 0);
- op1 = TREE_OPERAND (expr, 1);
- gcc_assert (POINTER_TYPE_P (TREE_TYPE (op0)));
-
- /* If the offset is not a non-negative integer constant that fits
- in a HOST_WIDE_INT, we cannot handle it here. */
- if (!host_integerp (op1, 1))
- return false;
-
- /* Make sure the bit-offset also fits. */
- rhsunitoffset = TREE_INT_CST_LOW (op1);
- rhsoffset = rhsunitoffset * BITS_PER_UNIT;
- if (rhsunitoffset != rhsoffset / BITS_PER_UNIT)
- return false;
-
- get_constraint_for (op0, &temp);
-
- for (i = 0; VEC_iterate (ce_s, lhsc, i, c); i++)
- for (j = 0; VEC_iterate (ce_s, temp, j, c2); j++)
- {
- if (c2->type == ADDRESSOF && rhsoffset != 0)
- {
- varinfo_t temp = get_varinfo (c2->var);
-
- /* An access one after the end of an array is valid,
- so simply punt on accesses we cannot resolve. */
- temp = first_vi_for_offset (temp, rhsoffset);
- if (temp == NULL)
- continue;
- c2->var = temp->id;
- c2->offset = 0;
- }
- else
- c2->offset = rhsoffset;
- process_constraint (new_constraint (*c, *c2));
- }
-
- VEC_free (ce_s, heap, temp);
-
- return true;
-}
-
/* Create a constraint ID = OP. */
static void
RHS. */
static void
-handle_rhs_call (tree rhs)
+handle_rhs_call (gimple stmt)
{
- tree arg;
- call_expr_arg_iterator iter;
+ unsigned i;
+
+ for (i = 0; i < gimple_call_num_args (stmt); ++i)
+ {
+ tree arg = gimple_call_arg (stmt, i);
- FOR_EACH_CALL_EXPR_ARG (arg, iter, rhs)
- /* Find those pointers being passed, and make sure they end up
- pointing to anything. */
- if (could_have_pointers (arg))
- make_escape_constraint (arg);
+ /* Find those pointers being passed, and make sure they end up
+ pointing to anything. */
+ if (could_have_pointers (arg))
+ make_escape_constraint (arg);
+ }
/* The static chain escapes as well. */
- if (CALL_EXPR_STATIC_CHAIN (rhs))
- make_escape_constraint (CALL_EXPR_STATIC_CHAIN (rhs));
+ if (gimple_call_chain (stmt))
+ make_escape_constraint (gimple_call_chain (stmt));
}
/* For non-IPA mode, generate constraints necessary for a call
const function that returns a pointer in the statement STMT. */
static void
-handle_const_call (tree stmt)
+handle_const_call (gimple stmt)
{
- tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
- tree call = get_call_expr_in (stmt);
+ tree lhs = gimple_call_lhs (stmt);
VEC(ce_s, heap) *lhsc = NULL;
struct constraint_expr rhsc;
- unsigned int j;
+ unsigned int j, k;
struct constraint_expr *lhsp;
- tree arg, tmpvar;
- call_expr_arg_iterator iter;
+ tree tmpvar;
struct constraint_expr tmpc;
get_constraint_for (lhs, &lhsc);
/* If this is a nested function then it can return anything. */
- if (CALL_EXPR_STATIC_CHAIN (call))
+ if (gimple_call_chain (stmt))
{
rhsc.var = anything_id;
rhsc.offset = 0;
process_constraint (new_constraint (tmpc, rhsc));
/* May return arguments. */
- FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
- if (could_have_pointers (arg))
- {
- VEC(ce_s, heap) *argc = NULL;
- struct constraint_expr *argp;
- int i;
-
- get_constraint_for (arg, &argc);
- for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++)
- process_constraint (new_constraint (tmpc, *argp));
- VEC_free (ce_s, heap, argc);
- }
+ for (k = 0; k < gimple_call_num_args (stmt); ++k)
+ {
+ tree arg = gimple_call_arg (stmt, k);
+
+ if (could_have_pointers (arg))
+ {
+ VEC(ce_s, heap) *argc = NULL;
+ struct constraint_expr *argp;
+ int i;
+
+ get_constraint_for (arg, &argc);
+ for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++)
+ process_constraint (new_constraint (tmpc, *argp));
+ VEC_free (ce_s, heap, argc);
+ }
+ }
for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
process_constraint (new_constraint (*lhsp, tmpc));
pure function in statement STMT. */
static void
-handle_pure_call (tree stmt)
+handle_pure_call (gimple stmt)
{
- tree call = get_call_expr_in (stmt);
- tree arg;
- call_expr_arg_iterator iter;
+ unsigned i;
/* 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);
+ for (i = 0; i < gimple_call_num_args (stmt); ++i)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+
+ 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 (gimple_call_chain (stmt))
+ make_constraint_to (callused_id, gimple_call_chain (stmt));
/* If the call returns a pointer it may point to reachable memory
from the arguments. Not so for malloc functions though. */
- if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
- && could_have_pointers (GIMPLE_STMT_OPERAND (stmt, 0))
- && !(call_expr_flags (call) & ECF_MALLOC))
+ if (gimple_call_lhs (stmt)
+ && could_have_pointers (gimple_call_lhs (stmt))
+ && !(gimple_call_flags (stmt) & ECF_MALLOC))
{
- tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
+ tree lhs = gimple_call_lhs (stmt);
VEC(ce_s, heap) *lhsc = NULL;
struct constraint_expr rhsc;
struct constraint_expr *lhsp;
get_constraint_for (lhs, &lhsc);
/* If this is a nested function then it can return anything. */
- if (CALL_EXPR_STATIC_CHAIN (call))
+ if (gimple_call_chain (stmt))
{
rhsc.var = anything_id;
rhsc.offset = 0;
when building alias sets and computing alias grouping heuristics. */
static void
-find_func_aliases (tree origt)
+find_func_aliases (gimple origt)
{
- tree call, t = origt;
+ gimple t = origt;
VEC(ce_s, heap) *lhsc = NULL;
VEC(ce_s, heap) *rhsc = NULL;
struct constraint_expr *c;
enum escape_type stmt_escape_type;
- if (TREE_CODE (t) == RETURN_EXPR && TREE_OPERAND (t, 0))
- t = TREE_OPERAND (t, 0);
-
/* Now build constraints expressions. */
- if (TREE_CODE (t) == PHI_NODE)
+ if (gimple_code (t) == GIMPLE_PHI)
{
- gcc_assert (!AGGREGATE_TYPE_P (TREE_TYPE (PHI_RESULT (t))));
+ gcc_assert (!AGGREGATE_TYPE_P (TREE_TYPE (gimple_phi_result (t))));
/* Only care about pointers and structures containing
pointers. */
- if (could_have_pointers (PHI_RESULT (t)))
+ if (could_have_pointers (gimple_phi_result (t)))
{
- int i;
+ size_t i;
unsigned int j;
/* For a phi node, assign all the arguments to
the result. */
- get_constraint_for (PHI_RESULT (t), &lhsc);
- for (i = 0; i < PHI_NUM_ARGS (t); i++)
+ get_constraint_for (gimple_phi_result (t), &lhsc);
+ for (i = 0; i < gimple_phi_num_args (t); i++)
{
tree rhstype;
tree strippedrhs = PHI_ARG_DEF (t, i);
STRIP_NOPS (strippedrhs);
rhstype = TREE_TYPE (strippedrhs);
- get_constraint_for (PHI_ARG_DEF (t, i), &rhsc);
+ get_constraint_for (gimple_phi_arg_def (t, i), &rhsc);
for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
{
}
}
/* In IPA mode, we need to generate constraints to pass call
- arguments through their calls. There are two cases, either a
- GIMPLE_MODIFY_STMT when we are returning a value, or just a plain
- CALL_EXPR when we are not.
+ arguments through their calls. There are two cases,
+ either a GIMPLE_CALL returning a value, or just a plain
+ GIMPLE_CALL when we are not.
In non-ipa mode, we need to generate constraints for each
pointer passed by address. */
- else if ((call = get_call_expr_in (t)) != NULL_TREE)
+ else if (is_gimple_call (t))
{
- int flags = call_expr_flags (call);
if (!in_ipa_mode)
{
+ int flags = gimple_call_flags (t);
+
/* Const functions can return their arguments and addresses
of global memory but not of escaped memory. */
if (flags & ECF_CONST)
{
- if (TREE_CODE (t) == GIMPLE_MODIFY_STMT
- && could_have_pointers (GIMPLE_STMT_OPERAND (t, 1)))
+ if (gimple_call_lhs (t)
+ && could_have_pointers (gimple_call_lhs (t)))
handle_const_call (t);
}
+ /* 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. */
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), flags);
+ if (gimple_call_lhs (t)
+ && could_have_pointers (gimple_call_lhs (t)))
+ handle_lhs_call (gimple_call_lhs (t), flags);
}
- /* 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
- do not compute call-used memory separately we cannot do
- something special here. */
- else if (TREE_CODE (t) == GIMPLE_MODIFY_STMT)
+ else
{
- handle_rhs_call (GIMPLE_STMT_OPERAND (t, 1));
- if (could_have_pointers (GIMPLE_STMT_OPERAND (t, 1)))
- handle_lhs_call (GIMPLE_STMT_OPERAND (t, 0), flags);
+ handle_rhs_call (t);
+ if (gimple_call_lhs (t)
+ && could_have_pointers (gimple_call_lhs (t)))
+ handle_lhs_call (gimple_call_lhs (t), flags);
}
- else
- handle_rhs_call (t);
}
else
{
tree lhsop;
- tree rhsop;
- tree arg;
- call_expr_arg_iterator iter;
varinfo_t fi;
int i = 1;
+ size_t j;
tree decl;
- if (TREE_CODE (t) == GIMPLE_MODIFY_STMT)
- {
- lhsop = GIMPLE_STMT_OPERAND (t, 0);
- rhsop = GIMPLE_STMT_OPERAND (t, 1);
- }
- else
- {
- lhsop = NULL;
- rhsop = t;
- }
- decl = get_callee_fndecl (rhsop);
+
+ lhsop = gimple_call_lhs (t);
+ decl = gimple_call_fndecl (t);
/* If we can directly resolve the function being called, do so.
Otherwise, it must be some sort of indirect expression that
we should still be able to handle. */
if (decl)
- {
- fi = get_vi_for_tree (decl);
- }
+ fi = get_vi_for_tree (decl);
else
{
- decl = CALL_EXPR_FN (rhsop);
+ decl = gimple_call_fn (t);
fi = get_vi_for_tree (decl);
}
/* Assign all the passed arguments to the appropriate incoming
parameters of the function. */
-
- FOR_EACH_CALL_EXPR_ARG (arg, iter, rhsop)
+ for (j = 0; j < gimple_call_num_args (t); j++)
{
struct constraint_expr lhs ;
struct constraint_expr *rhsp;
+ tree arg = gimple_call_arg (t, j);
get_constraint_for (arg, &rhsc);
if (TREE_CODE (decl) != FUNCTION_DECL)
}
}
}
- /* Otherwise, just a regular assignment statement. */
- else if (TREE_CODE (t) == GIMPLE_MODIFY_STMT)
+ /* Otherwise, just a regular assignment statement. Only care about
+ operations with pointer result, others are dealt with as escape
+ points if they have pointer operands. */
+ else if (is_gimple_assign (t)
+ && could_have_pointers (gimple_assign_lhs (t)))
{
- tree lhsop = GIMPLE_STMT_OPERAND (t, 0);
- tree rhsop = GIMPLE_STMT_OPERAND (t, 1);
- int i;
+ /* Otherwise, just a regular assignment statement. */
+ tree lhsop = gimple_assign_lhs (t);
+ tree rhsop = (gimple_num_ops (t) == 2) ? gimple_assign_rhs1 (t) : NULL;
- if (AGGREGATE_TYPE_P (TREE_TYPE (lhsop))
- && AGGREGATE_TYPE_P (TREE_TYPE (rhsop)))
- {
- do_structure_copy (lhsop, rhsop);
- }
+ if (rhsop && AGGREGATE_TYPE_P (TREE_TYPE (lhsop)))
+ do_structure_copy (lhsop, rhsop);
else
{
- /* Only care about operations with pointers, structures
- containing pointers, dereferences, and call expressions. */
- if (could_have_pointers (lhsop)
- || TREE_CODE (rhsop) == CALL_EXPR)
+ unsigned int j;
+ struct constraint_expr temp;
+ get_constraint_for (lhsop, &lhsc);
+
+ if (gimple_assign_rhs_code (t) == POINTER_PLUS_EXPR)
+ get_constraint_for_ptr_offset (gimple_assign_rhs1 (t),
+ gimple_assign_rhs2 (t), &rhsc);
+ else if ((CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (t))
+ && !(POINTER_TYPE_P (gimple_expr_type (t))
+ && !POINTER_TYPE_P (TREE_TYPE (rhsop))))
+ || gimple_assign_single_p (t))
+ get_constraint_for (rhsop, &rhsc);
+ else
{
- get_constraint_for (lhsop, &lhsc);
- switch (TREE_CODE_CLASS (TREE_CODE (rhsop)))
- {
- /* RHS that consist of unary operations,
- exceptional types, or bare decls/constants, get
- handled directly by get_constraint_for. */
- case tcc_reference:
- case tcc_declaration:
- case tcc_constant:
- case tcc_exceptional:
- case tcc_expression:
- case tcc_vl_exp:
- case tcc_unary:
- {
- unsigned int j;
-
- get_constraint_for (rhsop, &rhsc);
- for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
- {
- struct constraint_expr *c2;
- unsigned int k;
-
- for (k = 0; VEC_iterate (ce_s, rhsc, k, c2); k++)
- process_constraint (new_constraint (*c, *c2));
- }
-
- }
- break;
+ temp.type = ADDRESSOF;
+ temp.var = anything_id;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, rhsc, &temp);
+ }
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
+ {
+ struct constraint_expr *c2;
+ unsigned int k;
- case tcc_binary:
- {
- /* For pointer arithmetic of the form
- PTR + CST, we can simply use PTR's
- constraint because pointer arithmetic is
- not allowed to go out of bounds. */
- if (handle_ptr_arith (lhsc, rhsop))
- break;
- }
- /* FALLTHRU */
-
- /* Otherwise, walk each operand. Notice that we
- can't use the operand interface because we need
- to process expressions other than simple operands
- (e.g. INDIRECT_REF, ADDR_EXPR, CALL_EXPR). */
- default:
- for (i = 0; i < TREE_OPERAND_LENGTH (rhsop); i++)
- {
- tree op = TREE_OPERAND (rhsop, i);
- unsigned int j;
-
- gcc_assert (VEC_length (ce_s, rhsc) == 0);
- get_constraint_for (op, &rhsc);
- for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
- {
- struct constraint_expr *c2;
- while (VEC_length (ce_s, rhsc) > 0)
- {
- c2 = VEC_last (ce_s, rhsc);
- process_constraint (new_constraint (*c, *c2));
- VEC_pop (ce_s, rhsc);
- }
- }
- }
- }
+ for (k = 0; VEC_iterate (ce_s, rhsc, k, c2); k++)
+ process_constraint (new_constraint (*c, *c2));
}
}
}
- else if (TREE_CODE (t) == CHANGE_DYNAMIC_TYPE_EXPR)
+ else if (gimple_code (t) == GIMPLE_CHANGE_DYNAMIC_TYPE)
{
unsigned int j;
- get_constraint_for (CHANGE_DYNAMIC_TYPE_LOCATION (t), &lhsc);
+ get_constraint_for (gimple_cdt_location (t), &lhsc);
for (j = 0; VEC_iterate (ce_s, lhsc, j, c); ++j)
get_varinfo (c->var)->no_tbaa_pruning = true;
}
stmt_escape_type = is_escape_site (t);
if (stmt_escape_type == ESCAPE_STORED_IN_GLOBAL)
{
- tree rhs;
- gcc_assert (TREE_CODE (t) == GIMPLE_MODIFY_STMT);
- rhs = GIMPLE_STMT_OPERAND (t, 1);
- if (TREE_CODE (rhs) == ADDR_EXPR)
+ gcc_assert (is_gimple_assign (t));
+ if (gimple_assign_rhs_code (t) == ADDR_EXPR)
{
+ tree rhs = gimple_assign_rhs1 (t);
tree base = get_base_address (TREE_OPERAND (rhs, 0));
if (base
&& (!DECL_P (base)
|| !is_global_var (base)))
make_escape_constraint (rhs);
}
- else if (TREE_CODE (rhs) == SSA_NAME
- && POINTER_TYPE_P (TREE_TYPE (rhs)))
- make_escape_constraint (rhs);
- else if (could_have_pointers (rhs))
- make_escape_constraint (rhs);
+ else if (get_gimple_rhs_class (gimple_assign_rhs_code (t))
+ == GIMPLE_SINGLE_RHS)
+ {
+ if (could_have_pointers (gimple_assign_rhs1 (t)))
+ make_escape_constraint (gimple_assign_rhs1 (t));
+ }
+ else
+ gcc_unreachable ();
}
else if (stmt_escape_type == ESCAPE_BAD_CAST)
{
- tree rhs;
- gcc_assert (TREE_CODE (t) == GIMPLE_MODIFY_STMT);
- rhs = GIMPLE_STMT_OPERAND (t, 1);
- gcc_assert (CONVERT_EXPR_P (rhs)
- || TREE_CODE (rhs) == VIEW_CONVERT_EXPR);
- rhs = TREE_OPERAND (rhs, 0);
- make_escape_constraint (rhs);
+ gcc_assert (is_gimple_assign (t));
+ gcc_assert (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (t))
+ || gimple_assign_rhs_code (t) == VIEW_CONVERT_EXPR);
+ make_escape_constraint (gimple_assign_rhs1 (t));
}
else if (stmt_escape_type == ESCAPE_TO_ASM)
{
- tree link;
- int i;
- for (i = 0, link = ASM_OUTPUTS (t); link; i++, link = TREE_CHAIN (link))
+ unsigned i;
+ for (i = 0; i < gimple_asm_noutputs (t); ++i)
{
- tree op = TREE_VALUE (link);
+ tree op = TREE_VALUE (gimple_asm_output_op (t, i));
if (op && could_have_pointers (op))
/* Strictly we'd only need the constraints from ESCAPED and
NONLOCAL. */
make_escape_constraint (op);
}
- for (i = 0, link = ASM_INPUTS (t); link; i++, link = TREE_CHAIN (link))
+ for (i = 0; i < gimple_asm_ninputs (t); ++i)
{
- tree op = TREE_VALUE (link);
+ tree op = TREE_VALUE (gimple_asm_input_op (t, i));
if (op && could_have_pointers (op))
/* Strictly we'd only need the constraint to ESCAPED. */
make_escape_constraint (op);
number of statements re-scanned. It's not really necessary to
re-scan *all* statements. */
if (!in_ipa_mode)
- mark_stmt_modified (origt);
+ gimple_set_modified (origt, true);
VEC_free (ce_s, heap, rhsc);
VEC_free (ce_s, heap, lhsc);
}
struct fieldoff
{
- /* Type of the field. */
- tree type;
+ /* Offset from the base of the base containing object to this field. */
+ HOST_WIDE_INT offset;
/* Size, in bits, of the field. */
- tree size;
+ unsigned HOST_WIDE_INT size;
- /* Field. */
- tree decl;
+ unsigned has_unknown_size : 1;
- /* Offset from the base of the base containing object to this field. */
- HOST_WIDE_INT offset;
+ unsigned may_have_pointers : 1;
};
typedef struct fieldoff fieldoff_s;
else if (foa->offset > fob->offset)
return 1;
- foasize = TREE_INT_CST_LOW (foa->size);
- fobsize = TREE_INT_CST_LOW (fob->size);
+ foasize = foa->size;
+ fobsize = fob->size;
if (foasize < fobsize)
- return - 1;
+ return -1;
else if (foasize > fobsize)
return 1;
return 0;
OFFSET is used to keep track of the offset in this entire
structure, rather than just the immediately containing structure.
- Returns the number of fields pushed.
-
- HAS_UNION is set to true if we find a union type as a field of
- TYPE. */
+ Returns the number of fields pushed. */
static int
push_fields_onto_fieldstack (tree type, VEC(fieldoff_s,heap) **fieldstack,
- HOST_WIDE_INT offset, bool *has_union)
+ HOST_WIDE_INT offset)
{
tree field;
int count = 0;
{
bool push = false;
int pushed = 0;
+ HOST_WIDE_INT foff = bitpos_of_field (field);
- if (has_union
- && (TREE_CODE (TREE_TYPE (field)) == QUAL_UNION_TYPE
- || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE))
- *has_union = true;
-
- if (!var_can_have_subvars (field))
+ if (!var_can_have_subvars (field)
+ || TREE_CODE (TREE_TYPE (field)) == QUAL_UNION_TYPE
+ || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
push = true;
else if (!(pushed = push_fields_onto_fieldstack
- (TREE_TYPE (field),
- fieldstack,
- offset + bitpos_of_field (field),
- has_union))
+ (TREE_TYPE (field), fieldstack, offset + foff))
&& (DECL_SIZE (field)
&& !integer_zerop (DECL_SIZE (field))))
/* Empty structures may have actual size, like in C++. So
if (push)
{
- fieldoff_s *pair;
-
- pair = VEC_safe_push (fieldoff_s, heap, *fieldstack, NULL);
- pair->type = TREE_TYPE (field);
- pair->size = DECL_SIZE (field);
- pair->decl = field;
- pair->offset = offset + bitpos_of_field (field);
- count++;
+ fieldoff_s *pair = NULL;
+ bool has_unknown_size = false;
+
+ if (!VEC_empty (fieldoff_s, *fieldstack))
+ pair = VEC_last (fieldoff_s, *fieldstack);
+
+ if (!DECL_SIZE (field)
+ || !host_integerp (DECL_SIZE (field), 1))
+ has_unknown_size = true;
+
+ /* If adjacent fields do not contain pointers merge them. */
+ if (pair
+ && !pair->may_have_pointers
+ && !could_have_pointers (field)
+ && !pair->has_unknown_size
+ && !has_unknown_size
+ && pair->offset + (HOST_WIDE_INT)pair->size == offset + foff)
+ {
+ pair = VEC_last (fieldoff_s, *fieldstack);
+ pair->size += TREE_INT_CST_LOW (DECL_SIZE (field));
+ }
+ else
+ {
+ pair = VEC_safe_push (fieldoff_s, heap, *fieldstack, NULL);
+ pair->offset = offset + foff;
+ pair->has_unknown_size = has_unknown_size;
+ if (!has_unknown_size)
+ pair->size = TREE_INT_CST_LOW (DECL_SIZE (field));
+ else
+ pair->size = -1;
+ pair->may_have_pointers = could_have_pointers (field);
+ count++;
+ }
}
else
count += pushed;
vi = new_var_info (decl, index, name);
vi->decl = decl;
vi->offset = 0;
- vi->has_union = 0;
vi->size = 1;
vi->fullsize = count_num_arguments (decl, &is_varargs) + 1;
insert_vi_for_tree (vi->decl, vi);
stats.total_vars++;
/* If it's varargs, we don't know how many arguments it has, so we
- can't do much.
- */
+ can't do much. */
if (is_varargs)
{
vi->fullsize = ~0;
VEC_safe_push (varinfo_t, heap, varmap, argvi);
argvi->offset = i;
argvi->size = 1;
+ argvi->is_full_var = true;
argvi->fullsize = vi->fullsize;
- argvi->has_union = false;
insert_into_field_list_sorted (vi, argvi);
stats.total_vars ++;
if (arg)
resultvi->offset = i;
resultvi->size = 1;
resultvi->fullsize = vi->fullsize;
- resultvi->has_union = false;
+ resultvi->is_full_var = true;
insert_into_field_list_sorted (vi, resultvi);
stats.total_vars ++;
if (DECL_RESULT (decl))
{
unsigned int index = VEC_length (varinfo_t, varmap);
varinfo_t vi;
- tree decltype = TREE_TYPE (decl);
- tree declsize = DECL_P (decl) ? DECL_SIZE (decl) : TYPE_SIZE (decltype);
- bool notokay = false;
- bool hasunion;
+ tree decl_type = TREE_TYPE (decl);
+ tree declsize = DECL_P (decl) ? DECL_SIZE (decl) : TYPE_SIZE (decl_type);
bool is_global = DECL_P (decl) ? is_global_var (decl) : false;
VEC (fieldoff_s,heap) *fieldstack = NULL;
if (TREE_CODE (decl) == FUNCTION_DECL && in_ipa_mode)
return create_function_info_for (decl, name);
- hasunion = TREE_CODE (decltype) == UNION_TYPE
- || TREE_CODE (decltype) == QUAL_UNION_TYPE;
- if (var_can_have_subvars (decl) && use_field_sensitive && !hasunion)
- {
- push_fields_onto_fieldstack (decltype, &fieldstack, 0, &hasunion);
- if (hasunion)
- {
- VEC_free (fieldoff_s, heap, fieldstack);
- notokay = true;
- }
- }
+ if (var_can_have_subvars (decl) && use_field_sensitive
+ && (!var_ann (decl)
+ || var_ann (decl)->noalias_state == 0)
+ && (!var_ann (decl)
+ || !var_ann (decl)->is_heapvar))
+ push_fields_onto_fieldstack (decl_type, &fieldstack, 0);
/* If the variable doesn't have subvars, we may end up needing to
sort the field list and create fake variables for all the
vi = new_var_info (decl, index, name);
vi->decl = decl;
vi->offset = 0;
- vi->has_union = hasunion;
if (!declsize
- || TREE_CODE (declsize) != INTEGER_CST
- || TREE_CODE (decltype) == UNION_TYPE
- || TREE_CODE (decltype) == QUAL_UNION_TYPE)
+ || !host_integerp (declsize, 1))
{
vi->is_unknown_size_var = true;
vi->fullsize = ~0;
VEC_safe_push (varinfo_t, heap, varmap, vi);
if (is_global && (!flag_whole_program || !in_ipa_mode)
&& could_have_pointers (decl))
- make_constraint_from (vi, escaped_id);
+ {
+ if (var_ann (decl)
+ && var_ann (decl)->noalias_state == NO_ALIAS_ANYTHING)
+ make_constraint_from (vi, vi->id);
+ else
+ make_constraint_from (vi, escaped_id);
+ }
stats.total_vars++;
if (use_field_sensitive
- && !notokay
&& !vi->is_unknown_size_var
&& var_can_have_subvars (decl)
&& VEC_length (fieldoff_s, fieldstack) > 1
{
unsigned int newindex = VEC_length (varinfo_t, varmap);
fieldoff_s *fo = NULL;
+ bool notokay = false;
unsigned int i;
for (i = 0; !notokay && VEC_iterate (fieldoff_s, fieldstack, i, fo); i++)
{
- if (! fo->size
- || TREE_CODE (fo->size) != INTEGER_CST
+ if (fo->has_unknown_size
|| fo->offset < 0)
{
notokay = true;
vi->is_unknown_size_var = 1;
vi->fullsize = ~0;
vi->size = ~0;
+ vi->is_full_var = true;
VEC_free (fieldoff_s, heap, fieldstack);
return index;
}
- vi->size = TREE_INT_CST_LOW (fo->size);
+ vi->size = fo->size;
vi->offset = fo->offset;
for (i = VEC_length (fieldoff_s, fieldstack) - 1;
i >= 1 && VEC_iterate (fieldoff_s, fieldstack, i, fo);
newindex = VEC_length (varinfo_t, varmap);
if (dump_file)
{
- if (fo->decl)
- asprintf (&tempname, "%s.%s",
- vi->name, alias_get_name (fo->decl));
- else
- asprintf (&tempname, "%s." HOST_WIDE_INT_PRINT_DEC,
- vi->name, fo->offset);
+ asprintf (&tempname, "%s." HOST_WIDE_INT_PRINT_DEC
+ "+" HOST_WIDE_INT_PRINT_DEC,
+ vi->name, fo->offset, fo->size);
newname = ggc_strdup (tempname);
free (tempname);
}
newvi = new_var_info (decl, newindex, newname);
newvi->offset = fo->offset;
- newvi->size = TREE_INT_CST_LOW (fo->size);
+ newvi->size = fo->size;
newvi->fullsize = vi->fullsize;
insert_into_field_list (vi, newvi);
VEC_safe_push (varinfo_t, heap, varmap, newvi);
if (is_global && (!flag_whole_program || !in_ipa_mode)
- && (!fo->decl || could_have_pointers (fo->decl)))
+ && fo->may_have_pointers)
make_constraint_from (newvi, escaped_id);
stats.total_vars++;
}
}
+ else
+ vi->is_full_var = true;
VEC_free (fieldoff_s, heap, fieldstack);
heapvar_insert (t, heapvar);
ann = get_var_ann (heapvar);
+ ann->is_heapvar = 1;
if (flag_argument_noalias == 1)
ann->noalias_state = NO_ALIAS;
else if (flag_argument_noalias == 2)
make_constraint_from (p, nonlocal_id);
}
}
+
+ /* Add a constraint for a result decl that is passed by reference. */
+ if (DECL_RESULT (cfun->decl)
+ && DECL_BY_REFERENCE (DECL_RESULT (cfun->decl)))
+ {
+ varinfo_t p, result_vi = get_vi_for_tree (DECL_RESULT (cfun->decl));
+
+ for (p = result_vi; p; p = p->next)
+ make_constraint_from (p, nonlocal_id);
+ }
+
+ /* Add a constraint for the incoming static chain parameter. */
+ if (cfun->static_chain_decl != NULL_TREE)
+ {
+ varinfo_t p, chain_vi = get_vi_for_tree (cfun->static_chain_decl);
+
+ for (p = chain_vi; p; p = p->next)
+ make_constraint_from (p, nonlocal_id);
+ }
}
/* Structure used to put solution bitmaps in a hashtable so they can
IS_DEREFED is true if PTR was directly dereferenced, which we use to
help determine whether we are we are allowed to prune using TBAA.
If NO_TBAA_PRUNING is true, we do not perform any TBAA pruning of
- the from set. */
+ the from set. Returns the number of pruned variables. */
-static void
+static unsigned
set_uids_in_ptset (tree ptr, bitmap into, bitmap from, bool is_derefed,
bool no_tbaa_pruning)
{
unsigned int i;
bitmap_iterator bi;
+ unsigned pruned = 0;
gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr)));
type-based pruning disabled. */
if (vi->is_artificial_var
|| !is_derefed
- || no_tbaa_pruning)
+ || no_tbaa_pruning
+ || vi->no_tbaa_pruning)
bitmap_set_bit (into, DECL_UID (vi->decl));
else
{
if (may_alias_p (SSA_NAME_VAR (ptr), mem_alias_set,
vi->decl, var_alias_set, true))
bitmap_set_bit (into, DECL_UID (vi->decl));
+ else
+ ++pruned;
}
}
}
+
+ return pruned;
}
static bool have_alias_info = false;
-/* The list of SMT's that are in use by our pointer variables. This
- is the set of SMT's for all pointers that can point to anything. */
-static bitmap used_smts;
+/* Emit a note for the pointer initialization point DEF. */
-/* Due to the ordering of points-to set calculation and SMT
- calculation being a bit co-dependent, we can't just calculate SMT
- used info whenever we want, we have to calculate it around the time
- that find_what_p_points_to is called. */
+static void
+emit_pointer_definition (tree ptr, bitmap visited)
+{
+ gimple def = SSA_NAME_DEF_STMT (ptr);
+ if (gimple_code (def) == GIMPLE_PHI)
+ {
+ use_operand_p argp;
+ ssa_op_iter oi;
-/* Mark which SMT's are in use by points-to anything variables. */
+ FOR_EACH_PHI_ARG (argp, def, oi, SSA_OP_USE)
+ {
+ tree arg = USE_FROM_PTR (argp);
+ if (TREE_CODE (arg) == SSA_NAME)
+ {
+ if (bitmap_set_bit (visited, SSA_NAME_VERSION (arg)))
+ emit_pointer_definition (arg, visited);
+ }
+ else
+ inform (0, "initialized from %qE", arg);
+ }
+ }
+ else if (!gimple_nop_p (def))
+ inform (gimple_location (def), "initialized from here");
+}
-void
-set_used_smts (void)
+/* Emit a strict aliasing warning for dereferencing the pointer PTR. */
+
+static void
+emit_alias_warning (tree ptr)
{
- int i;
- varinfo_t vi;
- used_smts = BITMAP_ALLOC (&pta_obstack);
+ gimple use;
+ imm_use_iterator ui;
+ bool warned = false;
- for (i = 0; VEC_iterate (varinfo_t, varmap, i, vi); i++)
+ FOR_EACH_IMM_USE_STMT (use, ui, ptr)
{
- tree var = vi->decl;
- varinfo_t withsolution = get_varinfo (find (i));
- tree smt;
- var_ann_t va;
- struct ptr_info_def *pi = NULL;
-
- /* For parm decls, the pointer info may be under the default
- def. */
- if (TREE_CODE (vi->decl) == PARM_DECL
- && gimple_default_def (cfun, var))
- pi = SSA_NAME_PTR_INFO (gimple_default_def (cfun, var));
- else if (TREE_CODE (var) == SSA_NAME)
- pi = SSA_NAME_PTR_INFO (var);
-
- /* Skip the special variables and those that can't be aliased. */
- if (vi->is_special_var
- || !SSA_VAR_P (var)
- || (pi && !pi->memory_tag_needed)
- || (TREE_CODE (var) == VAR_DECL && !may_be_aliased (var))
- || !POINTER_TYPE_P (TREE_TYPE (var)))
- continue;
-
- if (TREE_CODE (var) == SSA_NAME)
- var = SSA_NAME_VAR (var);
-
- va = var_ann (var);
- if (!va)
- continue;
+ tree deref = NULL_TREE;
- smt = va->symbol_mem_tag;
- if (smt && bitmap_bit_p (withsolution->solution, anything_id))
- bitmap_set_bit (used_smts, DECL_UID (smt));
+ if (gimple_has_lhs (use))
+ {
+ tree lhs = get_base_address (gimple_get_lhs (use));
+ if (lhs
+ && INDIRECT_REF_P (lhs)
+ && TREE_OPERAND (lhs, 0) == ptr)
+ deref = lhs;
+ }
+ if (gimple_assign_single_p (use))
+ {
+ tree rhs = get_base_address (gimple_assign_rhs1 (use));
+ if (rhs
+ && INDIRECT_REF_P (rhs)
+ && TREE_OPERAND (rhs, 0) == ptr)
+ deref = rhs;
+ }
+ else if (is_gimple_call (use))
+ {
+ unsigned i;
+ for (i = 0; i < gimple_call_num_args (use); ++i)
+ {
+ tree op = get_base_address (gimple_call_arg (use, i));
+ if (op
+ && INDIRECT_REF_P (op)
+ && TREE_OPERAND (op, 0) == ptr)
+ deref = op;
+ }
+ }
+ if (deref
+ && !TREE_NO_WARNING (deref))
+ {
+ TREE_NO_WARNING (deref) = 1;
+ warned |= warning_at (gimple_location (use), OPT_Wstrict_aliasing,
+ "dereferencing pointer %qD does break "
+ "strict-aliasing rules", SSA_NAME_VAR (ptr));
+ }
+ }
+ if (warned)
+ {
+ bitmap visited = BITMAP_ALLOC (NULL);
+ emit_pointer_definition (ptr, visited);
+ BITMAP_FREE (visited);
}
}
else
{
struct ptr_info_def *pi = get_ptr_info (p);
- unsigned int i;
+ unsigned int i, pruned;
bitmap_iterator bi;
bool was_pt_anything = false;
bitmap finished_solution;
finished_solution = BITMAP_GGC_ALLOC ();
stats.points_to_sets_created++;
- set_uids_in_ptset (p, finished_solution, vi->solution,
- pi->is_dereferenced,
- vi->no_tbaa_pruning);
+ pruned = set_uids_in_ptset (p, finished_solution, vi->solution,
+ pi->is_dereferenced,
+ vi->no_tbaa_pruning);
result = shared_bitmap_lookup (finished_solution);
if (!result)
}
if (bitmap_empty_p (pi->pt_vars))
- pi->pt_vars = NULL;
+ {
+ pi->pt_vars = NULL;
+ if (pruned > 0
+ && !pi->pt_null
+ && pi->is_dereferenced
+ && warn_strict_aliasing > 0
+ && !SSA_NAME_IS_DEFAULT_DEF (p))
+ {
+ if (dump_file && dump_flags & TDF_DETAILS)
+ {
+ fprintf (dump_file, "alias warning for ");
+ print_generic_expr (dump_file, p, 0);
+ fprintf (dump_file, "\n");
+ }
+ emit_alias_warning (p);
+ }
+ }
return true;
}
static void
init_alias_vars (void)
{
+ use_field_sensitive = (MAX_FIELDS_FOR_FIELD_SENSITIVE > 1);
+
bitmap_obstack_initialize (&pta_obstack);
bitmap_obstack_initialize (&oldpta_obstack);
bitmap_obstack_initialize (&predbitmap_obstack);
/* Now walk all statements and derive aliases. */
FOR_EACH_BB (bb)
{
- block_stmt_iterator bsi;
- tree phi;
+ gimple_stmt_iterator gsi;
- for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
- if (is_gimple_reg (PHI_RESULT (phi)))
- find_func_aliases (phi);
-
- for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
+ for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
- tree stmt = bsi_stmt (bsi);
-
- find_func_aliases (stmt);
+ gimple phi = gsi_stmt (gsi);
- /* The information in CHANGE_DYNAMIC_TYPE_EXPR nodes has now
- been captured, and we can remove them. */
- if (TREE_CODE (stmt) == CHANGE_DYNAMIC_TYPE_EXPR)
- bsi_remove (&bsi, true);
- else
- bsi_next (&bsi);
+ if (is_gimple_reg (gimple_phi_result (phi)))
+ find_func_aliases (phi);
}
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ find_func_aliases (gsi_stmt (gsi));
}
free_var_substitution_info (si);
build_succ_graph ();
+
+ if (dump_file && (dump_flags & TDF_GRAPH))
+ dump_constraint_graph (dump_file);
+
move_complex_constraints (graph);
if (dump_file)
static bool
gate_ipa_pta (void)
{
- return (flag_unit_at_a_time != 0
- && flag_ipa_pta
+ return (flag_ipa_pta
/* Don't bother doing anything if the program has errors. */
&& !(errorcount || sorrycount));
}
FOR_EACH_BB_FN (bb, func)
{
- block_stmt_iterator bsi;
- tree phi;
+ gimple_stmt_iterator gsi;
- for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+ for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
{
- if (is_gimple_reg (PHI_RESULT (phi)))
- {
- find_func_aliases (phi);
- }
- }
+ gimple phi = gsi_stmt (gsi);
- for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
- {
- tree stmt = bsi_stmt (bsi);
- find_func_aliases (stmt);
+ if (is_gimple_reg (gimple_phi_result (phi)))
+ find_func_aliases (phi);
}
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ find_func_aliases (gsi_stmt (gsi));
}
current_function_decl = old_func_decl;
pop_cfun ();
heapvar_for_stmt = NULL;
}
-
#include "gt-tree-ssa-structalias.h"