#include "obstack.h"
#include "bitmap.h"
#include "flags.h"
-#include "rtl.h"
-#include "tm_p.h"
-#include "hard-reg-set.h"
#include "basic-block.h"
#include "output.h"
#include "tree.h"
#include "tree-flow.h"
#include "tree-inline.h"
-#include "diagnostic.h"
+#include "diagnostic-core.h"
#include "toplev.h"
#include "gimple.h"
#include "hashtab.h"
keep the set of called functions for indirect calls.
And probably more. */
-
-static GTY ((if_marked ("tree_map_marked_p"), param_is (struct tree_map)))
+static GTY ((if_marked ("tree_map_marked_p"), param_is (struct heapvar_map)))
htab_t heapvar_for_stmt;
static bool use_field_sensitive = true;
struct heapvar_map *h;
void **loc;
- h = GGC_NEW (struct heapvar_map);
+ h = ggc_alloc_heapvar_map ();
h->map.base.from = from;
h->offset = offset;
h->map.hash = heapvar_map_hash (h);
/* Print out constraint C to stderr. */
-void
+DEBUG_FUNCTION void
debug_constraint (constraint_t c)
{
dump_constraint (stderr, c);
/* Print out all constraints to stderr. */
-void
+DEBUG_FUNCTION void
debug_constraints (void)
{
dump_constraints (stderr, 0);
/* Print out the constraint graph to stderr. */
-void
+DEBUG_FUNCTION void
debug_constraint_graph (void)
{
dump_constraint_graph (stderr);
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;
/* For parameters, get at the points-to set for the actual parm
decl. */
if (TREE_CODE (t) == SSA_NAME
- && TREE_CODE (SSA_NAME_VAR (t)) == PARM_DECL
+ && (TREE_CODE (SSA_NAME_VAR (t)) == PARM_DECL
+ || TREE_CODE (SSA_NAME_VAR (t)) == RESULT_DECL)
&& SSA_NAME_IS_DEFAULT_DEF (t))
{
get_constraint_for_ssa_var (SSA_NAME_VAR (t), results, address_p);
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
&0->a.b */
forzero = t;
while (handled_component_p (forzero)
- || INDIRECT_REF_P (forzero))
+ || INDIRECT_REF_P (forzero)
+ || TREE_CODE (forzero) == MEM_REF)
forzero = TREE_OPERAND (forzero, 0);
if (CONSTANT_CLASS_P (forzero) && integer_zerop (forzero))
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. 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;
+ 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);
{
switch (TREE_CODE (t))
{
- case INDIRECT_REF:
+ case MEM_REF:
{
- get_constraint_for_1 (TREE_OPERAND (t, 0), results, address_p);
+ get_constraint_for_ptr_offset (TREE_OPERAND (t, 0),
+ TREE_OPERAND (t, 1), results);
do_deref (results);
return;
}
process_constraint (new_constraint (lhs, rhs));
}
-/* Create a new artificial heap variable with NAME and make a
- constraint from it to LHS. Return the created variable. */
+/* Create a new artificial heap variable with NAME.
+ 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);
}
if (TREE_CODE (decl) == SSA_NAME)
{
if (TREE_CODE (decl) == SSA_NAME
- && TREE_CODE (SSA_NAME_VAR (decl)) == PARM_DECL
+ && (TREE_CODE (SSA_NAME_VAR (decl)) == PARM_DECL
+ || TREE_CODE (SSA_NAME_VAR (decl)) == RESULT_DECL)
&& SSA_NAME_IS_DEFAULT_DEF (decl))
decl = SSA_NAME_VAR (decl);
return get_vi_for_tree (decl);
/* va_end doesn't have any effect that matters. */
case BUILT_IN_VA_END:
return;
+ /* Alternate return. Simply give up for now. */
+ case BUILT_IN_RETURN:
+ {
+ fi = NULL;
+ if (!in_ipa_mode
+ || !(fi = get_vi_for_tree (cfun->decl)))
+ make_constraint_from (get_varinfo (escaped_id), anything_id);
+ else if (in_ipa_mode
+ && fi != NULL)
+ {
+ struct constraint_expr lhs, rhs;
+ lhs = get_function_part_constraint (fi, fi_result);
+ rhs.var = anything_id;
+ rhs.offset = 0;
+ rhs.type = SCALAR;
+ process_constraint (new_constraint (lhs, rhs));
+ }
+ return;
+ }
/* printf-style functions may have hooks to set pointers to
point to somewhere into the generated string. Leave them
for a later excercise... */
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
- && could_have_pointers (lhsop))
+ && type_could_have_pointers (TREE_TYPE (lhsop)))
{
struct constraint_expr rhs;
struct constraint_expr *lhsp;
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);
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 (gimple_assign_rhs_code (t) == BIT_AND_EXPR
+ && TREE_CODE (gimple_assign_rhs2 (t)) == INTEGER_CST)
+ {
+ /* Aligning a pointer via a BIT_AND_EXPR is offsetting
+ the pointer. Handle it by offsetting it by UNKNOWN. */
+ get_constraint_for_ptr_offset (gimple_assign_rhs1 (t),
+ NULL_TREE, &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))))
tem = TREE_OPERAND (tem, 0);
if ((DECL_P (tem)
&& !auto_var_in_fn_p (tem, cfun->decl))
- || INDIRECT_REF_P (tem))
+ || INDIRECT_REF_P (tem)
+ || (TREE_CODE (tem) == MEM_REF
+ && !(TREE_CODE (TREE_OPERAND (tem, 0)) == ADDR_EXPR
+ && auto_var_in_fn_p
+ (TREE_OPERAND (TREE_OPERAND (tem, 0), 0), cfun->decl))))
{
struct constraint_expr lhsc, *rhsp;
unsigned i;
tem = TREE_OPERAND (tem, 0);
if ((DECL_P (tem)
&& !auto_var_in_fn_p (tem, cfun->decl))
- || INDIRECT_REF_P (tem))
+ || INDIRECT_REF_P (tem)
+ || (TREE_CODE (tem) == MEM_REF
+ && !(TREE_CODE (TREE_OPERAND (tem, 0)) == ADDR_EXPR
+ && auto_var_in_fn_p
+ (TREE_OPERAND (TREE_OPERAND (tem, 0), 0), cfun->decl))))
{
struct constraint_expr lhs, *rhsp;
unsigned i;
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;
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)
+ (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
&& !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 = 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))
/* 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);
prev_vi = argvi;
}
- return vi->id;
+ return vi;
}
bool notokay = false;
unsigned int i;
- push_fields_onto_fieldstack (decl_type, &fieldstack, 0);
+ 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
/* Print the points-to solution for VAR to stdout. */
-void
+DEBUG_FUNCTION void
debug_solution_for_var (unsigned int var)
{
dump_solution_for_var (stdout, var);
/* For parameters, get at the points-to set for the actual parm
decl. */
if (TREE_CODE (p) == SSA_NAME
- && TREE_CODE (SSA_NAME_VAR (p)) == PARM_DECL
+ && (TREE_CODE (SSA_NAME_VAR (p)) == PARM_DECL
+ || TREE_CODE (SSA_NAME_VAR (p)) == RESULT_DECL)
&& SSA_NAME_IS_DEFAULT_DEF (p))
lookup_p = SSA_NAME_VAR (p);
pt->vars_contains_restrict = vars_contains_restrict;
}
+/* Set the points-to solution *PT to point only to the variable VAR. */
+
+void
+pt_solution_set_var (struct pt_solution *pt, tree var)
+{
+ memset (pt, 0, sizeof (struct pt_solution));
+ pt->vars = BITMAP_GGC_ALLOC ();
+ bitmap_set_bit (pt->vars, DECL_UID (var));
+ pt->vars_contains_global = is_global_var (var);
+}
+
/* Computes the union of the points-to solutions *DEST and *SRC and
stores the result in *DEST. This changes the points-to bitmap
of *DEST and thus may not be used if that might be shared.
/* Debug points-to information to stderr. */
-void
+DEBUG_FUNCTION void
debug_sa_points_to_info (void)
{
dump_sa_points_to_info (stderr);
return (optimize
&& flag_ipa_pta
/* Don't bother doing anything if the program has errors. */
- && !(errorcount || sorrycount));
+ && !seen_error ());
}
/* IPA PTA solutions for ESCAPED. */
/* 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. */
|| node->clone_of)
continue;
- create_function_info_for (node->decl,
- cgraph_node_name (node));
+ 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 constraints for global variables and their initializers. */
for (var = varpool_nodes; var; var = var->next)
- get_vi_for_tree (var->decl);
+ {
+ 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);
+ }
if (dump_file)
{
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;