static const char *
alias_get_name (tree decl)
{
- const char *res;
+ const char *res = get_name (decl);
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 (((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 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 ((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;
+ 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. But in IPA mode we also
+ process global initializers, so verify at least. */
+ || (TREE_CODE (t) == CONSTRUCTOR
+ && CONSTRUCTOR_NELTS (t) == 0)))
+ {
+ temp.var = nothing_id;
temp.type = ADDRESSOF;
temp.offset = 0;
VEC_safe_push (ce_s, heap, *results, &temp);
constraint from it to LHS. Return the created variable. */
static varinfo_t
-make_heapvar_for (varinfo_t lhs, const char *name)
+make_constraint_from_heapvar (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;
else if (TREE_CODE (fi->decl) == FUNCTION_DECL)
{
varinfo_t ai = first_vi_for_offset (fi, part);
- c.var = ai ? ai->id : anything_id;
+ if (ai)
+ c.var = ai->id;
+ else
+ c.var = anything_id;
c.offset = 0;
c.type = SCALAR;
}
{
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);
-
- /* 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
+ /* Find those pointers being passed, and make sure they end up
+ pointing to anything. */
+ if (could_have_pointers (arg))
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 (gimple stmt, tree lhs, int flags, VEC(ce_s, heap) *rhsc,
- tree fndecl)
+handle_lhs_call (tree lhs, int flags, VEC(ce_s, heap) *rhsc, tree fndecl)
{
VEC(ce_s, heap) *lhsc = NULL;
get_constraint_for (lhs, &lhsc);
- /* 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)
+
+ if (flags & ECF_MALLOC)
{
varinfo_t vi;
- struct constraint_expr tmpc;
- rhsc = NULL;
- vi = make_heapvar_for (get_vi_for_tree (lhs), "HEAP");
+ vi = make_constraint_from_heapvar (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);
}
-
- process_all_all_constraints (lhsc, rhsc);
-
+ 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);
+ }
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 (t, gimple_call_lhs (t), flags, rhsc, fndecl);
+ handle_lhs_call (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)
- && type_could_have_pointers (TREE_TYPE (gimple_assign_lhs (t))))
+ && could_have_pointers (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 void
-process_ipa_clobber (varinfo_t fi, tree ptr)
+static varinfo_t
+first_or_preceding_vi_for_offset (varinfo_t start,
+ unsigned HOST_WIDE_INT offset)
{
- 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);
-}
+ /* 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);
-/* 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. */
+ /* 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;
-static void
-find_func_clobbers (gimple origt)
-{
- gimple t = origt;
- VEC(ce_s, heap) *lhsc = NULL;
- VEC(ce_s, heap) *rhsc = NULL;
- varinfo_t fi;
+ return start;
+}
- /* 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
static bool
push_fields_onto_fieldstack (tree type, VEC(fieldoff_s,heap) **fieldstack,
- HOST_WIDE_INT offset, bool must_have_pointers_p)
+ HOST_WIDE_INT offset)
{
tree field;
bool empty_p = true;
|| TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
push = true;
else if (!push_fields_onto_fieldstack
- (TREE_TYPE (field), fieldstack, offset + foff,
- must_have_pointers_p)
+ (TREE_TYPE (field), fieldstack, offset + foff)
&& (DECL_SIZE (field)
&& !integer_zerop (DECL_SIZE (field))))
/* Empty structures may have actual size, like in C++. So
&& !pair->has_unknown_size
&& !has_unknown_size
&& pair->offset + (HOST_WIDE_INT)pair->size == offset + foff
- && !must_have_pointers_p
&& !could_have_pointers (field))
{
pair->size += TREE_INT_CST_LOW (DECL_SIZE (field));
pair->size = TREE_INT_CST_LOW (DECL_SIZE (field));
else
pair->size = -1;
- pair->may_have_pointers
- = must_have_pointers_p || could_have_pointers (field);
+ pair->may_have_pointers = could_have_pointers (field);
pair->only_restrict_pointers
= (!has_unknown_size
&& POINTER_TYPE_P (TREE_TYPE (field))
/* Creation function node for DECL, using NAME, and return the index
of the variable we've created for the function. */
-static varinfo_t
+static unsigned int
create_function_info_for (tree decl, const char *name)
{
struct function *fn = DECL_STRUCT_FUNCTION (decl);
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;
+ return vi->id;
}
vi = new_var_info (decl, name);
vi->offset = 0;
vi->size = ~0;
- }
- else
- {
- vi->fullsize = TREE_INT_CST_LOW (declsize);
- vi->size = vi->fullsize;
- }
-
- insert_vi_for_tree (vi->decl, vi);
-
- /* ??? 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)
- {
- /* Mark global restrict qualified 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. */
- 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 (vi->decl))
- {
- VEC (ce_s, heap) *rhsc = NULL;
- struct constraint_expr lhs, *rhsp;
- unsigned i;
- get_constraint_for (DECL_INITIAL (vi->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))
- {
- 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));
- }
- 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);
- }
+ 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. */
bool notokay = false;
unsigned int i;
- push_fields_onto_fieldstack (decl_type, &fieldstack, 0,
- TREE_PUBLIC (decl)
- || DECL_EXTERNAL (decl)
- || TREE_ADDRESSABLE (decl));
+ 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
const char *newname = "NULL";
char *tempname;
- vi->offset = fo->offset;
- vi->may_have_pointers = fo->may_have_pointers;
- if (vi->is_global_var
- && vi->may_have_pointers)
+ if (dump_file)
{
asprintf (&tempname, "%s." HOST_WIDE_INT_PRINT_DEC
"+" HOST_WIDE_INT_PRINT_DEC, name, fo->offset, fo->size);
{
varinfo_t vi = get_varinfo (i);
if (!vi->may_have_pointers)
- {
- gcc_assert (find (i) == i
- || !(vi = get_varinfo (find (i)))->may_have_pointers);
- /* ??? See create_variable_info_for.
- gcc_assert (bitmap_empty_p (vi->solution)); */
- continue;
- }
+ continue;
dump_solution_for_var (outfile, i);
}
}
/* 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", 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");
- }
+ fprintf (dump_file,
+ "Generating constraints for %s\n",
+ cgraph_node_name (node));
func = DECL_STRUCT_FUNCTION (node->decl);
old_func_decl = current_function_decl;