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;
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;
}
{
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
/* If we are returning a value, assign it to the result. */
lhsop = gimple_call_lhs (t);
if (lhsop
- && type_could_have_pointers (TREE_TYPE (lhsop)))
+ && could_have_pointers (lhsop))
{
struct constraint_expr rhs;
struct constraint_expr *lhsp;
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);
+ 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);
+}
- /* 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;
+/* 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. */
- return start;
-}
+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;
+ /* 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
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;
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;
+ }
+ 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);
+ }
}
/* Collect field information. */
const char *newname = "NULL";
char *tempname;
- if (dump_file)
+ vi->offset = fo->offset;
+ vi->may_have_pointers = fo->may_have_pointers;
+ if (vi->is_global_var
+ && vi->may_have_pointers)
{
asprintf (&tempname, "%s." HOST_WIDE_INT_PRINT_DEC
"+" HOST_WIDE_INT_PRINT_DEC, name, fo->offset, fo->size);
|| node->clone_of)
continue;
- vi = create_function_info_for (node->decl,
- alias_get_name (node->decl));
-
- /* Associate the varinfo node with all aliases. */
- for (alias = node->same_body; alias; alias = alias->next)
- insert_vi_for_tree (alias->decl, vi);
+ create_function_info_for (node->decl,
+ cgraph_node_name (node));
}
/* Create constraints for global variables and their initializers. */
for (var = varpool_nodes; var; var = var->next)
- {
- struct varpool_node *alias;
- varinfo_t vi;
-
- vi = get_vi_for_tree (var->decl);
-
- /* Associate the varinfo node with all aliases. */
- for (alias = var->extra_name; alias; alias = alias->next)
- insert_vi_for_tree (alias->decl, vi);
- }
+ get_vi_for_tree (var->decl);
if (dump_file)
{