#include "tree.h"
#include "tree-flow.h"
#include "tree-inline.h"
+#include "varray.h"
#include "diagnostic.h"
#include "toplev.h"
#include "gimple.h"
/* True if this field may contain pointers. */
unsigned int may_have_pointers : 1;
- /* True if this field has only restrict qualified pointers. */
- unsigned int only_restrict_pointers : 1;
-
/* True if this represents a global variable. */
unsigned int is_global_var : 1;
ret->is_heap_var = false;
ret->is_restrict_var = false;
ret->may_have_pointers = true;
- ret->only_restrict_pointers = false;
ret->is_global_var = (t == NULL_TREE);
ret->is_fn_info = false;
if (t && DECL_P (t))
ret->oldsolution = BITMAP_ALLOC (&oldpta_obstack);
ret->next = NULL;
- stats.total_vars++;
-
VEC_safe_push (varinfo_t, heap, varmap, ret);
return ret;
unsigned int j;
bitmap_iterator bi;
HOST_WIDE_INT loff = c->lhs.offset;
- bool escaped_p = false;
/* Our IL does not allow this. */
gcc_assert (c->rhs.offset == 0);
unsigned int t;
HOST_WIDE_INT fieldoffset = v->offset + loff;
+ /* If v is a global variable then this is an escape point. */
+ if (v->is_global_var)
+ {
+ t = find (escaped_id);
+ if (add_graph_edge (graph, t, rhs)
+ && bitmap_ior_into (get_varinfo (t)->solution, sol)
+ && !TEST_BIT (changed, t))
+ {
+ SET_BIT (changed, t);
+ changed_count++;
+ }
+ }
+
+ if (v->is_special_var)
+ continue;
+
if (v->is_full_var)
fieldoffset = v->offset;
else if (loff != 0)
{
if (v->may_have_pointers)
{
- /* If v is a global variable then this is an escape point. */
- if (v->is_global_var
- && !escaped_p)
- {
- t = find (escaped_id);
- if (add_graph_edge (graph, t, rhs)
- && bitmap_ior_into (get_varinfo (t)->solution, sol)
- && !TEST_BIT (changed, t))
- {
- SET_BIT (changed, t);
- changed_count++;
- }
- /* Enough to let rhs escape once. */
- escaped_p = true;
- }
-
- if (v->is_special_var)
- break;
-
t = find (v->id);
if (add_graph_edge (graph, t, rhs)
&& bitmap_ior_into (get_varinfo (t)->solution, sol)
if (TREE_CODE (type) == ARRAY_TYPE)
return type_could_have_pointers (TREE_TYPE (type));
- /* A function or method can consume pointers.
- ??? We could be more precise here. */
- if (TREE_CODE (type) == FUNCTION_TYPE
- || TREE_CODE (type) == METHOD_TYPE)
- return true;
-
return AGGREGATE_TYPE_P (type);
}
&& ((TREE_CODE (t) == INTEGER_CST
&& integer_zerop (t))
/* The only valid CONSTRUCTORs in gimple with pointer typed
- elements are zero-initializer. But in IPA mode we also
- process global initializers, so verify at least. */
- || (TREE_CODE (t) == CONSTRUCTOR
- && CONSTRUCTOR_NELTS (t) == 0)))
+ elements are zero-initializer. */
+ || TREE_CODE (t) == CONSTRUCTOR))
{
temp.var = nothing_id;
temp.type = ADDRESSOF;
get_constraint_for_ssa_var (t, results, address_p);
return;
}
- case CONSTRUCTOR:
- {
- unsigned int i;
- tree val;
- VEC (ce_s, heap) *tmp = NULL;
- FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (t), i, val)
- {
- struct constraint_expr *rhsp;
- unsigned j;
- get_constraint_for_1 (val, &tmp, address_p);
- for (j = 0; VEC_iterate (ce_s, tmp, j, rhsp); ++j)
- VEC_safe_push (ce_s, heap, *results, rhsp);
- VEC_truncate (ce_s, tmp, 0);
- }
- VEC_free (ce_s, heap, tmp);
- /* We do not know whether the constructor was complete,
- so technically we have to add &NOTHING or &ANYTHING
- like we do for an empty constructor as well. */
- return;
- }
default:;
}
break;
else if (TREE_CODE (fi->decl) == FUNCTION_DECL)
{
varinfo_t ai = first_vi_for_offset (fi, part);
- if (ai)
- c.var = ai->id;
- else
- c.var = anything_id;
+ c.var = ai ? ai->id : anything_id;
c.offset = 0;
c.type = SCALAR;
}
}
+/* Insert the varinfo FIELD into the field list for BASE, at the front
+ of the list. */
+
+static void
+insert_into_field_list (varinfo_t base, varinfo_t field)
+{
+ varinfo_t prev = base;
+ varinfo_t curr = base->next;
+
+ field->next = curr;
+ prev->next = field;
+}
+
/* This structure is used during pushing fields onto the fieldstack
to track the offset of the field, since bitpos_of_field gives it
relative to its immediate containing type, and we want it relative
OFFSET is used to keep track of the offset in this entire
structure, rather than just the immediately containing structure.
- Returns false if the caller is supposed to handle the field we
- recursed for. */
+ Returns the number of fields pushed. */
-static bool
+static int
push_fields_onto_fieldstack (tree type, VEC(fieldoff_s,heap) **fieldstack,
HOST_WIDE_INT offset)
{
tree field;
- bool empty_p = true;
+ int count = 0;
if (TREE_CODE (type) != RECORD_TYPE)
- return false;
+ return 0;
/* If the vector of fields is growing too big, bail out early.
Callers check for VEC_length <= MAX_FIELDS_FOR_FIELD_SENSITIVE, make
sure this fails. */
if (VEC_length (fieldoff_s, *fieldstack) > MAX_FIELDS_FOR_FIELD_SENSITIVE)
- return false;
+ return 0;
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL)
{
bool push = false;
+ int pushed = 0;
HOST_WIDE_INT foff = bitpos_of_field (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 (!push_fields_onto_fieldstack
- (TREE_TYPE (field), fieldstack, offset + foff)
+ else if (!(pushed = push_fields_onto_fieldstack
+ (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 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
- && !could_have_pointers (field))
+ && 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
= (!has_unknown_size
&& POINTER_TYPE_P (TREE_TYPE (field))
&& TYPE_RESTRICT (TREE_TYPE (field)));
+ count++;
}
}
-
- empty_p = false;
+ else
+ count += pushed;
}
- return !empty_p;
+ return count;
}
/* Count the number of arguments DECL has, and set IS_VARARGS to true
vi->fullsize = ~0;
insert_vi_for_tree (vi->decl, vi);
+ stats.total_vars++;
+
prev_vi = vi;
/* Create a variable for things the function clobbers and one for
gcc_assert (prev_vi->offset < clobbervi->offset);
prev_vi->next = clobbervi;
prev_vi = clobbervi;
+ stats.total_vars++;
asprintf (&tempname, "%s.use", name);
newname = ggc_strdup (tempname);
gcc_assert (prev_vi->offset < usevi->offset);
prev_vi->next = usevi;
prev_vi = usevi;
+ stats.total_vars++;
}
/* And one for the static chain. */
gcc_assert (prev_vi->offset < chainvi->offset);
prev_vi->next = chainvi;
prev_vi = chainvi;
+ stats.total_vars++;
insert_vi_for_tree (fn->static_chain_decl, chainvi);
}
gcc_assert (prev_vi->offset < resultvi->offset);
prev_vi->next = resultvi;
prev_vi = resultvi;
+ stats.total_vars++;
if (DECL_RESULT (decl))
insert_vi_for_tree (DECL_RESULT (decl), resultvi);
}
gcc_assert (prev_vi->offset < argvi->offset);
prev_vi->next = argvi;
prev_vi = argvi;
+ stats.total_vars++;
if (arg)
{
insert_vi_for_tree (arg, argvi);
gcc_assert (prev_vi->offset < argvi->offset);
prev_vi->next = argvi;
prev_vi = argvi;
+ stats.total_vars++;
}
return vi->id;
This will also create any varinfo structures necessary for fields
of DECL. */
-static varinfo_t
-create_variable_info_for_1 (tree decl, const char *name)
+static unsigned int
+create_variable_info_for (tree decl, const char *name)
{
- varinfo_t vi, newvi;
+ varinfo_t vi;
tree decl_type = TREE_TYPE (decl);
tree declsize = DECL_P (decl) ? DECL_SIZE (decl) : TYPE_SIZE (decl_type);
VEC (fieldoff_s,heap) *fieldstack = NULL;
- fieldoff_s *fo;
- unsigned int i;
+ if (var_can_have_subvars (decl) && use_field_sensitive)
+ 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
+ fields. */
+ vi = new_var_info (decl, name);
+ vi->offset = 0;
+ vi->may_have_pointers = could_have_pointers (decl);
if (!declsize
|| !host_integerp (declsize, 1))
{
- vi = new_var_info (decl, name);
- vi->offset = 0;
- vi->size = ~0;
- vi->fullsize = ~0;
vi->is_unknown_size_var = true;
- vi->is_full_var = true;
- vi->may_have_pointers = could_have_pointers (decl);
- return vi;
- }
-
- /* Collect field information. */
- if (use_field_sensitive
- && var_can_have_subvars (decl)
- /* ??? Force us to not use subfields for global initializers
- in IPA mode. Else we'd have to parse arbitrary initializers. */
- && !(in_ipa_mode
- && is_global_var (decl)
- && DECL_INITIAL (decl)))
- {
- fieldoff_s *fo = NULL;
- bool notokay = false;
- unsigned int i;
-
- push_fields_onto_fieldstack (decl_type, &fieldstack, 0);
-
- for (i = 0; !notokay && VEC_iterate (fieldoff_s, fieldstack, i, fo); i++)
- if (fo->has_unknown_size
- || fo->offset < 0)
- {
- notokay = true;
- break;
- }
-
- /* We can't sort them if we have a field with a variable sized type,
- which will make notokay = true. In that case, we are going to return
- without creating varinfos for the fields anyway, so sorting them is a
- waste to boot. */
- if (!notokay)
- {
- sort_fieldstack (fieldstack);
- /* Due to some C++ FE issues, like PR 22488, we might end up
- what appear to be overlapping fields even though they,
- in reality, do not overlap. Until the C++ FE is fixed,
- we will simply disable field-sensitivity for these cases. */
- notokay = check_for_overlaps (fieldstack);
- }
-
- if (notokay)
- VEC_free (fieldoff_s, heap, fieldstack);
+ vi->fullsize = ~0;
+ vi->size = ~0;
}
-
- /* If we didn't end up collecting sub-variables create a full
- variable for the decl. */
- if (VEC_length (fieldoff_s, fieldstack) <= 1
- || VEC_length (fieldoff_s, fieldstack) > MAX_FIELDS_FOR_FIELD_SENSITIVE)
+ else
{
- vi = new_var_info (decl, name);
- vi->offset = 0;
- vi->may_have_pointers = could_have_pointers (decl);
vi->fullsize = TREE_INT_CST_LOW (declsize);
vi->size = vi->fullsize;
- vi->is_full_var = true;
- VEC_free (fieldoff_s, heap, fieldstack);
- return vi;
- }
-
- vi = new_var_info (decl, name);
- vi->fullsize = TREE_INT_CST_LOW (declsize);
- for (i = 0, newvi = vi;
- VEC_iterate (fieldoff_s, fieldstack, i, fo);
- ++i, newvi = newvi->next)
- {
- const char *newname = "NULL";
- char *tempname;
-
- if (dump_file)
- {
- asprintf (&tempname, "%s." HOST_WIDE_INT_PRINT_DEC
- "+" HOST_WIDE_INT_PRINT_DEC, name, fo->offset, fo->size);
- newname = ggc_strdup (tempname);
- free (tempname);
- }
- newvi->name = newname;
- newvi->offset = fo->offset;
- newvi->size = fo->size;
- newvi->fullsize = vi->fullsize;
- newvi->may_have_pointers = fo->may_have_pointers;
- newvi->only_restrict_pointers = fo->only_restrict_pointers;
- if (i + 1 < VEC_length (fieldoff_s, fieldstack))
- newvi->next = new_var_info (decl, name);
}
- VEC_free (fieldoff_s, heap, fieldstack);
-
- return vi;
-}
-
-static unsigned int
-create_variable_info_for (tree decl, const char *name)
-{
- varinfo_t vi = create_variable_info_for_1 (decl, name);
- unsigned int id = vi->id;
-
- insert_vi_for_tree (decl, vi);
+ insert_vi_for_tree (vi->decl, vi);
- /* Create initial constraints for globals. */
- for (; vi; vi = vi->next)
+ /* ??? The setting of vi->may_have_pointers is too conservative here
+ and may get refined below. Thus we have superfluous constraints
+ here sometimes which triggers the commented assert in
+ dump_sa_points_to_info. */
+ if (vi->is_global_var
+ && vi->may_have_pointers)
{
- if (!vi->may_have_pointers
- || !vi->is_global_var)
- continue;
-
/* Mark global restrict qualified pointers. */
- if ((POINTER_TYPE_P (TREE_TYPE (decl))
- && TYPE_RESTRICT (TREE_TYPE (decl)))
- || vi->only_restrict_pointers)
+ if (POINTER_TYPE_P (TREE_TYPE (decl))
+ && TYPE_RESTRICT (TREE_TYPE (decl)))
make_constraint_from_restrict (vi, "GLOBAL_RESTRICT");
/* For escaped variables initialize them from nonlocal. */
IPA mode generate constraints for it. In non-IPA mode
the initializer from nonlocal is all we need. */
if (in_ipa_mode
- && DECL_INITIAL (decl))
+ && DECL_INITIAL (vi->decl))
{
VEC (ce_s, heap) *rhsc = NULL;
struct constraint_expr lhs, *rhsp;
unsigned i;
- get_constraint_for (DECL_INITIAL (decl), &rhsc);
+ get_constraint_for (DECL_INITIAL (vi->decl), &rhsc);
lhs.var = vi->id;
lhs.offset = 0;
lhs.type = SCALAR;
process_constraint (new_constraint (lhs, *rhsp));
}
VEC_free (ce_s, heap, rhsc);
+ /* ??? Force us to not use subfields. Else we'd have to parse
+ arbitrary initializers. */
+ VEC_free (fieldoff_s, heap, fieldstack);
+ }
+ }
+
+ stats.total_vars++;
+ if (use_field_sensitive
+ && !vi->is_unknown_size_var
+ && var_can_have_subvars (decl)
+ && VEC_length (fieldoff_s, fieldstack) > 1
+ && VEC_length (fieldoff_s, fieldstack) <= MAX_FIELDS_FOR_FIELD_SENSITIVE)
+ {
+ fieldoff_s *fo = NULL;
+ bool notokay = false;
+ unsigned int i;
+
+ for (i = 0; !notokay && VEC_iterate (fieldoff_s, fieldstack, i, fo); i++)
+ {
+ if (fo->has_unknown_size
+ || fo->offset < 0)
+ {
+ notokay = true;
+ break;
+ }
+ }
+
+ /* We can't sort them if we have a field with a variable sized type,
+ which will make notokay = true. In that case, we are going to return
+ without creating varinfos for the fields anyway, so sorting them is a
+ waste to boot. */
+ if (!notokay)
+ {
+ sort_fieldstack (fieldstack);
+ /* Due to some C++ FE issues, like PR 22488, we might end up
+ what appear to be overlapping fields even though they,
+ in reality, do not overlap. Until the C++ FE is fixed,
+ we will simply disable field-sensitivity for these cases. */
+ notokay = check_for_overlaps (fieldstack);
+ }
+
+
+ if (VEC_length (fieldoff_s, fieldstack) != 0)
+ fo = VEC_index (fieldoff_s, fieldstack, 0);
+
+ if (fo == NULL || notokay)
+ {
+ vi->is_unknown_size_var = 1;
+ vi->fullsize = ~0;
+ vi->size = ~0;
+ vi->is_full_var = true;
+ VEC_free (fieldoff_s, heap, fieldstack);
+ return vi->id;
+ }
+
+ vi->size = fo->size;
+ vi->offset = fo->offset;
+ vi->may_have_pointers = fo->may_have_pointers;
+ if (vi->is_global_var
+ && vi->may_have_pointers)
+ {
+ if (fo->only_restrict_pointers)
+ make_constraint_from_restrict (vi, "GLOBAL_RESTRICT");
+ }
+ for (i = VEC_length (fieldoff_s, fieldstack) - 1;
+ i >= 1 && VEC_iterate (fieldoff_s, fieldstack, i, fo);
+ i--)
+ {
+ varinfo_t newvi;
+ const char *newname = "NULL";
+ char *tempname;
+
+ if (dump_file)
+ {
+ 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, newname);
+ newvi->offset = fo->offset;
+ newvi->size = fo->size;
+ newvi->fullsize = vi->fullsize;
+ newvi->may_have_pointers = fo->may_have_pointers;
+ insert_into_field_list (vi, newvi);
+ if ((newvi->is_global_var || TREE_CODE (decl) == PARM_DECL)
+ && newvi->may_have_pointers)
+ {
+ if (fo->only_restrict_pointers)
+ make_constraint_from_restrict (newvi, "GLOBAL_RESTRICT");
+ if (newvi->is_global_var && !in_ipa_mode)
+ make_copy_constraint (newvi, nonlocal_id);
+ }
+
+ stats.total_vars++;
}
}
+ else
+ vi->is_full_var = true;
- return id;
+ VEC_free (fieldoff_s, heap, fieldstack);
+
+ return vi->id;
}
/* Print out the points-to solution for VAR to FILE. */
}
for (p = get_vi_for_tree (t); p; p = p->next)
- {
- if (p->may_have_pointers)
- make_constraint_from (p, nonlocal_id);
- if (p->only_restrict_pointers)
- make_constraint_from_restrict (p, "PARM_RESTRICT");
- }
+ if (p->may_have_pointers)
+ make_constraint_from (p, nonlocal_id);
if (POINTER_TYPE_P (TREE_TYPE (t))
&& TYPE_RESTRICT (TREE_TYPE (t)))
make_constraint_from_restrict (get_vi_for_tree (t), "PARM_RESTRICT");