#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)
static const char *
alias_get_name (tree decl)
{
- const char *res = get_name (decl);
+ const char *res;
char *temp;
int num_printed = 0;
+ if (DECL_ASSEMBLER_NAME_SET_P (decl))
+ res = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ else
+ res= get_name (decl);
if (res != NULL)
return res;
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);
}
static bool
could_have_pointers (tree t)
{
- return type_could_have_pointers (TREE_TYPE (t));
+ return (((TREE_CODE (t) == VAR_DECL
+ || TREE_CODE (t) == PARM_DECL
+ || TREE_CODE (t) == RESULT_DECL)
+ && (TREE_PUBLIC (t) || DECL_EXTERNAL (t) || TREE_ADDRESSABLE (t)))
+ || type_could_have_pointers (TREE_TYPE (t)));
}
/* Return the position, in bits, of FIELD_DECL from the beginning of its
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))
- /* The only valid CONSTRUCTORs in gimple with pointer typed
- elements are zero-initializer. */
- || TREE_CODE (t) == CONSTRUCTOR))
- {
- temp.var = nothing_id;
+ if ((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))
+ {
+ if (flag_delete_null_pointer_checks)
+ temp.var = nothing_id;
+ else
+ temp.var = anything_id;
temp.type = ADDRESSOF;
temp.offset = 0;
VEC_safe_push (ce_s, heap, *results, &temp);
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;
constraint from it to LHS. Return the created variable. */
static varinfo_t
-make_constraint_from_heapvar (varinfo_t lhs, const char *name)
+make_heapvar_for (varinfo_t lhs, const char *name)
{
varinfo_t vi;
tree heapvar = heapvar_lookup (lhs->decl, lhs->offset);
vi->is_full_var = true;
insert_vi_for_tree (heapvar, vi);
+ return vi;
+}
+
+/* Create a new artificial heap variable with NAME and make a
+ constraint from it to LHS. Return the created variable. */
+
+static varinfo_t
+make_constraint_from_heapvar (varinfo_t lhs, const char *name)
+{
+ varinfo_t vi = make_heapvar_for (lhs, name);
make_constraint_from (lhs, vi->id);
return vi;
{
struct constraint_expr rhsc;
unsigned i;
+ bool returns_uses = false;
for (i = 0; i < gimple_call_num_args (stmt); ++i)
{
tree arg = gimple_call_arg (stmt, i);
+ int flags = gimple_call_arg_flags (stmt, i);
- /* Find those pointers being passed, and make sure they end up
- pointing to anything. */
- if (could_have_pointers (arg))
+ /* If the argument is not used or it does not contain pointers
+ we can ignore it. */
+ if ((flags & EAF_UNUSED)
+ || !could_have_pointers (arg))
+ continue;
+
+ /* As we compute ESCAPED context-insensitive we do not gain
+ any precision with just EAF_NOCLOBBER but not EAF_NOESCAPE
+ set. The argument would still get clobbered through the
+ escape solution.
+ ??? We might get away with less (and more precise) constraints
+ if using a temporary for transitively closing things. */
+ if ((flags & EAF_NOCLOBBER)
+ && (flags & EAF_NOESCAPE))
+ {
+ varinfo_t uses = get_call_use_vi (stmt);
+ if (!(flags & EAF_DIRECT))
+ make_transitive_closure_constraints (uses);
+ make_constraint_to (uses->id, arg);
+ returns_uses = true;
+ }
+ else if (flags & EAF_NOESCAPE)
+ {
+ varinfo_t uses = get_call_use_vi (stmt);
+ varinfo_t clobbers = get_call_clobber_vi (stmt);
+ if (!(flags & EAF_DIRECT))
+ {
+ make_transitive_closure_constraints (uses);
+ make_transitive_closure_constraints (clobbers);
+ }
+ make_constraint_to (uses->id, arg);
+ make_constraint_to (clobbers->id, arg);
+ returns_uses = true;
+ }
+ else
make_escape_constraint (arg);
}
+ /* If we added to the calls uses solution make sure we account for
+ pointers to it to be returned. */
+ if (returns_uses)
+ {
+ rhsc.var = get_call_use_vi (stmt)->id;
+ rhsc.offset = 0;
+ rhsc.type = SCALAR;
+ VEC_safe_push (ce_s, heap, *results, &rhsc);
+ }
+
/* The static chain escapes as well. */
if (gimple_call_chain (stmt))
make_escape_constraint (gimple_call_chain (stmt));
the LHS point to global and escaped variables. */
static void
-handle_lhs_call (tree lhs, int flags, VEC(ce_s, heap) *rhsc, tree fndecl)
+handle_lhs_call (gimple stmt, tree lhs, int flags, VEC(ce_s, heap) *rhsc,
+ tree fndecl)
{
VEC(ce_s, heap) *lhsc = NULL;
get_constraint_for (lhs, &lhsc);
-
- if (flags & ECF_MALLOC)
+ /* If the store is to a global decl make sure to
+ add proper escape constraints. */
+ lhs = get_base_address (lhs);
+ if (lhs
+ && DECL_P (lhs)
+ && is_global_var (lhs))
+ {
+ struct constraint_expr tmpc;
+ tmpc.var = escaped_id;
+ tmpc.offset = 0;
+ tmpc.type = SCALAR;
+ VEC_safe_push (ce_s, heap, lhsc, &tmpc);
+ }
+
+ /* If the call returns an argument unmodified override the rhs
+ constraints. */
+ flags = gimple_call_return_flags (stmt);
+ if (flags & ERF_RETURNS_ARG
+ && (flags & ERF_RETURN_ARG_MASK) < gimple_call_num_args (stmt))
+ {
+ tree arg;
+ rhsc = NULL;
+ arg = gimple_call_arg (stmt, flags & ERF_RETURN_ARG_MASK);
+ get_constraint_for (arg, &rhsc);
+ process_all_all_constraints (lhsc, rhsc);
+ VEC_free (ce_s, heap, rhsc);
+ }
+ else if (flags & ERF_NOALIAS)
{
varinfo_t vi;
- vi = make_constraint_from_heapvar (get_vi_for_tree (lhs), "HEAP");
+ struct constraint_expr tmpc;
+ rhsc = NULL;
+ vi = make_heapvar_for (get_vi_for_tree (lhs), "HEAP");
/* We delay marking allocated storage global until we know if
it escapes. */
DECL_EXTERNAL (vi->decl) = 0;
vi->is_global_var = 0;
/* If this is not a real malloc call assume the memory was
- initialized and thus may point to global memory. All
+ initialized and thus may point to global memory. All
builtin functions with the malloc attribute behave in a sane way. */
if (!fndecl
|| DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
make_constraint_from (vi, nonlocal_id);
+ tmpc.var = vi->id;
+ tmpc.offset = 0;
+ tmpc.type = ADDRESSOF;
+ VEC_safe_push (ce_s, heap, rhsc, &tmpc);
}
- else if (VEC_length (ce_s, rhsc) > 0)
- {
- /* If the store is to a global decl make sure to
- add proper escape constraints. */
- lhs = get_base_address (lhs);
- if (lhs
- && DECL_P (lhs)
- && is_global_var (lhs))
- {
- struct constraint_expr tmpc;
- tmpc.var = escaped_id;
- tmpc.offset = 0;
- tmpc.type = SCALAR;
- VEC_safe_push (ce_s, heap, lhsc, &tmpc);
- }
- process_all_all_constraints (lhsc, rhsc);
- }
+
+ process_all_all_constraints (lhsc, rhsc);
+
VEC_free (ce_s, heap, lhsc);
}
handle_rhs_call (t, &rhsc);
if (gimple_call_lhs (t)
&& could_have_pointers (gimple_call_lhs (t)))
- handle_lhs_call (gimple_call_lhs (t), flags, rhsc, fndecl);
+ handle_lhs_call (t, gimple_call_lhs (t), flags, rhsc, fndecl);
VEC_free (ce_s, heap, rhsc);
}
else
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)))
+ && type_could_have_pointers (TREE_TYPE (gimple_assign_lhs (t))))
{
/* Otherwise, just a regular assignment statement. */
tree lhsop = gimple_assign_lhs (t);
OFFSET. If there is no such varinfo the varinfo directly preceding
OFFSET is returned. */
-static varinfo_t
-first_or_preceding_vi_for_offset (varinfo_t start,
- unsigned HOST_WIDE_INT offset)
+static void
+process_ipa_clobber (varinfo_t fi, tree ptr)
{
- /* If we cannot reach offset from start, lookup the first field
- and start from there. */
- if (start->offset > offset)
- start = lookup_vi_for_tree (start->decl);
-
- /* We may not find a variable in the field list with the actual
- offset when when we have glommed a structure to a variable.
- In that case, however, offset should still be within the size
- of the variable.
- If we got beyond the offset we look for return the field
- directly preceding offset which may be the last field. */
- while (start->next
- && offset >= start->offset
- && !((offset - start->offset) < start->size))
- start = start->next;
-
- return start;
+ VEC(ce_s, heap) *ptrc = NULL;
+ struct constraint_expr *c, lhs;
+ unsigned i;
+ get_constraint_for (ptr, &ptrc);
+ lhs = get_function_part_constraint (fi, fi_clobbers);
+ for (i = 0; VEC_iterate (ce_s, ptrc, i, c); i++)
+ process_constraint (new_constraint (lhs, *c));
+ VEC_free (ce_s, heap, ptrc);
}
-
-/* Insert the varinfo FIELD into the field list for BASE, at the front
- of the list. */
+/* Walk statement T setting up clobber and use constraints according to the
+ references found in T. This function is a main part of the
+ IPA constraint builder. */
static void
-insert_into_field_list (varinfo_t base, varinfo_t field)
+find_func_clobbers (gimple origt)
{
- varinfo_t prev = base;
- varinfo_t curr = base->next;
+ gimple t = origt;
+ VEC(ce_s, heap) *lhsc = NULL;
+ VEC(ce_s, heap) *rhsc = NULL;
+ varinfo_t fi;
- field->next = curr;
- prev->next = field;
-}
+ /* Add constraints for clobbered/used in IPA mode.
+ We are not interested in what automatic variables are clobbered
+ or used as we only use the information in the caller to which
+ they do not escape. */
+ gcc_assert (in_ipa_mode);
/* This structure is used during pushing fields onto the fieldstack
to track the offset of the field, since bitpos_of_field gives it
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. */
+ Returns false if the caller is supposed to handle the field we
+ recursed for. */
-static int
+static bool
push_fields_onto_fieldstack (tree type, VEC(fieldoff_s,heap) **fieldstack,
- HOST_WIDE_INT offset)
+ HOST_WIDE_INT offset, bool must_have_pointers_p)
{
tree field;
- int count = 0;
+ bool empty_p = true;
if (TREE_CODE (type) != RECORD_TYPE)
- return 0;
+ return false;
/* 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 0;
+ return false;
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 (!(pushed = push_fields_onto_fieldstack
- (TREE_TYPE (field), fieldstack, offset + foff))
+ else if (!push_fields_onto_fieldstack
+ (TREE_TYPE (field), fieldstack, offset + foff,
+ must_have_pointers_p)
&& (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)
+ && pair->offset + (HOST_WIDE_INT)pair->size == offset + foff
+ && !must_have_pointers_p
+ && !could_have_pointers (field))
{
- pair = VEC_last (fieldoff_s, *fieldstack);
pair->size += TREE_INT_CST_LOW (DECL_SIZE (field));
}
else
pair->size = TREE_INT_CST_LOW (DECL_SIZE (field));
else
pair->size = -1;
- pair->may_have_pointers = could_have_pointers (field);
+ pair->may_have_pointers
+ = must_have_pointers_p || could_have_pointers (field);
pair->only_restrict_pointers
= (!has_unknown_size
&& POINTER_TYPE_P (TREE_TYPE (field))
&& TYPE_RESTRICT (TREE_TYPE (field)));
- count++;
}
}
- else
- count += pushed;
+
+ empty_p = false;
}
- return count;
+ return !empty_p;
}
/* Count the number of arguments DECL has, and set IS_VARARGS to true
/* Creation function node for DECL, using NAME, and return the index
of the variable we've created for the function. */
-static unsigned int
+static varinfo_t
create_function_info_for (tree decl, const char *name)
{
struct function *fn = DECL_STRUCT_FUNCTION (decl);
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
stats.total_vars++;
}
- return vi->id;
+ return vi;
}
This will also create any varinfo structures necessary for fields
of DECL. */
-static unsigned int
-create_variable_info_for (tree decl, const char *name)
+static varinfo_t
+create_variable_info_for_1 (tree decl, const char *name)
{
- varinfo_t vi;
+ varinfo_t vi, newvi;
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->is_unknown_size_var = true;
- vi->fullsize = ~0;
+ vi = new_var_info (decl, name);
+ vi->offset = 0;
vi->size = ~0;
}
else
}
}
- stats.total_vars++;
+ /* Collect field information. */
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)
+ /* ??? 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,
+ TREE_PUBLIC (decl)
+ || DECL_EXTERNAL (decl)
+ || TREE_ADDRESSABLE (decl));
+
for (i = 0; !notokay && VEC_iterate (fieldoff_s, fieldstack, i, fo); i++)
- {
- if (fo->has_unknown_size
- || fo->offset < 0)
- {
- notokay = true;
- break;
- }
- }
+ 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
notokay = check_for_overlaps (fieldstack);
}
+ if (notokay)
+ VEC_free (fieldoff_s, heap, fieldstack);
+ }
- if (VEC_length (fieldoff_s, fieldstack) != 0)
- fo = VEC_index (fieldoff_s, fieldstack, 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)
+ {
+ 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;
+ }
- 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 = 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;
- 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");
+ asprintf (&tempname, "%s." HOST_WIDE_INT_PRINT_DEC
+ "+" HOST_WIDE_INT_PRINT_DEC, name, fo->offset, fo->size);
+ newname = ggc_strdup (tempname);
+ free (tempname);
}
- 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;
+ 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);
+ }
- 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)
+ 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);
+
+ /* Create initial constraints for globals. */
+ for (; vi; vi = vi->next)
+ {
+ 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)
+ make_constraint_from_restrict (vi, "GLOBAL_RESTRICT");
+
+ /* For escaped variables initialize them from nonlocal. */
+ if (!in_ipa_mode
+ || DECL_EXTERNAL (decl) || TREE_PUBLIC (decl))
+ make_copy_constraint (vi, nonlocal_id);
+
+ /* If this is a global variable with an initializer and we are in
+ 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))
+ {
+ VEC (ce_s, heap) *rhsc = NULL;
+ struct constraint_expr lhs, *rhsp;
+ unsigned i;
+ get_constraint_for (DECL_INITIAL (decl), &rhsc);
+ lhs.var = vi->id;
+ lhs.offset = 0;
+ lhs.type = SCALAR;
+ for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); ++i)
+ process_constraint (new_constraint (lhs, *rhsp));
+ /* If this is a variable that escapes from the unit
+ the initializer escapes as well. */
+ if (DECL_EXTERNAL (decl) || TREE_PUBLIC (decl))
{
- 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);
+ lhs.var = escaped_id;
+ lhs.offset = 0;
+ lhs.type = SCALAR;
+ for (i = 0; VEC_iterate (ce_s, rhsc, i, rhsp); ++i)
+ process_constraint (new_constraint (lhs, *rhsp));
}
-
- stats.total_vars++;
+ VEC_free (ce_s, heap, rhsc);
}
}
- else
- vi->is_full_var = true;
-
- VEC_free (fieldoff_s, heap, fieldstack);
- return vi->id;
+ return 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->may_have_pointers)
+ make_constraint_from (p, nonlocal_id);
+ if (p->only_restrict_pointers)
+ make_constraint_from_restrict (p, "PARM_RESTRICT");
+ }
if (POINTER_TYPE_P (TREE_TYPE (t))
&& TYPE_RESTRICT (TREE_TYPE (t)))
make_constraint_from_restrict (get_vi_for_tree (t), "PARM_RESTRICT");
/* Build the constraints. */
for (node = cgraph_nodes; node; node = node->next)
{
+ struct cgraph_node *alias;
+ varinfo_t vi;
+
/* Nodes without a body are not interesting. Especially do not
visit clones at this point for now - we get duplicate decls
there for inline clones at least. */
continue;
if (dump_file)
- fprintf (dump_file,
- "Generating constraints for %s\n",
- cgraph_node_name (node));
+ {
+ fprintf (dump_file,
+ "Generating constraints for %s", cgraph_node_name (node));
+ if (DECL_ASSEMBLER_NAME_SET_P (node->decl))
+ fprintf (dump_file, " (%s)",
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->decl)));
+ fprintf (dump_file, "\n");
+ }
func = DECL_STRUCT_FUNCTION (node->decl);
old_func_decl = current_function_decl;