934 lines
25 KiB
C
934 lines
25 KiB
C
/* Tree based Andersen points-to analysis
|
|
Copyright (C) 2002, 2003 Free Software Foundation, Inc.
|
|
Contributed by Daniel Berlin <dberlin@dberlin.org>
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "tm.h"
|
|
#include "ggc.h"
|
|
#include "bitmap.h"
|
|
#include "tree-alias-type.h"
|
|
#include "tree-alias-ander.h"
|
|
#include "flags.h"
|
|
#include "rtl.h"
|
|
#include "tm_p.h"
|
|
#include "hard-reg-set.h"
|
|
#include "basic-block.h"
|
|
#include "output.h"
|
|
#include "errors.h"
|
|
#include "expr.h"
|
|
#include "diagnostic.h"
|
|
#include "tree.h"
|
|
#include "tree-flow.h"
|
|
#include "tree-inline.h"
|
|
#include "varray.h"
|
|
#include "tree-gimple.h"
|
|
#include "splay-tree.h"
|
|
#include "engine/util.h"
|
|
#include "libcompat/regions.h"
|
|
#include "andersen_terms.h"
|
|
#include "cgraph.h"
|
|
#include "tree-pass.h"
|
|
|
|
|
|
/* Andersen's interprocedural points-to analysis.
|
|
This is a flow-insensitive, context insensitive algorithm.
|
|
|
|
This file is an implementation of the alias_ops structure used by
|
|
tree-alias-common.c to drive PTA analysis.
|
|
|
|
All these functions do is generate constraints for and through
|
|
libbanshee. When we query for a points-to set, we ask libbanshee
|
|
to solve the constraints and give us the answer. The terms of the
|
|
constraints are the aterms, which are an opaque data structure
|
|
that stores libbanshee specific data for the constraints.
|
|
|
|
The constraints to be generated come from andersen's paper. By
|
|
constraint, we mean something like "the points-to set of A must be
|
|
a subset or equal to the points-to set of B" or "the points-to set
|
|
of A must include Q". In order to avoid having to write all the
|
|
constraints directly in the code, we use helper functions such as
|
|
pta_assignment, pta_rvalue, etc, that generate the necessary
|
|
constraint terms for us, making for much more readable code.
|
|
|
|
One could replace libbanshee with some other constraint solving
|
|
engine, and you'd simply have to replace the implementation of the
|
|
pta_* functions, and provide replacements for the aterm specific
|
|
functions (like making a list of aterms, printing the label of an
|
|
aterm). However, libbanshee is extremely fast, and extremely low
|
|
memory usage, so one would be hard pressed to do better than it
|
|
anyway.
|
|
|
|
Understanding how constraint solving and what each constraint means is
|
|
beyond the scope of this documentation. See the libbanshee
|
|
documentation, and references therein for more enlightenment.
|
|
|
|
That said, our constraints inclusion constraints of set
|
|
expressions. Given the helper functions, the various inference
|
|
functions we implement should *look* relatively straightforward.
|
|
|
|
In order to save time during queries, we cache the resulting
|
|
points-to sets of each variable, rather than recalculate them
|
|
again and again. (libbanshee actually has it's own internal
|
|
caching, but the function call overhead for calling the solver is
|
|
non-trivial, given the number of queries).
|
|
|
|
Todo: Don't pass alias ops as first argument, just have a global
|
|
"current_alias_ops". */
|
|
|
|
static unsigned int id_num = 1;
|
|
static region andersen_rgn;
|
|
static void andersen_simple_assign (struct tree_alias_ops *,
|
|
alias_var, alias_var);
|
|
static void andersen_addr_assign (struct tree_alias_ops *,
|
|
alias_var, alias_var);
|
|
static void andersen_ptr_assign (struct tree_alias_ops *,
|
|
alias_var, alias_var);
|
|
static void andersen_op_assign (struct tree_alias_ops *,
|
|
alias_var, varray_type, tree, bitmap);
|
|
static void andersen_heap_assign (struct tree_alias_ops *, alias_var);
|
|
static void andersen_assign_ptr (struct tree_alias_ops *,
|
|
alias_var, alias_var);
|
|
static void andersen_function_def (struct tree_alias_ops *, alias_var,
|
|
varray_type, alias_var);
|
|
static int andersen_function_call (struct tree_alias_ops *, alias_var,
|
|
alias_var, varray_type, bitmap);
|
|
static void andersen_init (struct tree_alias_ops *);
|
|
static int print_out_result (splay_tree_node, void *);
|
|
static void andersen_cleanup (struct tree_alias_ops *);
|
|
static bool andersen_may_alias (struct tree_alias_ops *, alias_var,
|
|
alias_var);
|
|
static bool andersen_same_points_to_set (struct tree_alias_ops *,
|
|
alias_var, alias_var);
|
|
static bool andersen_empty_points_to_set (struct tree_alias_ops *,
|
|
alias_var);
|
|
static alias_var andersen_add_var (struct tree_alias_ops *, tree);
|
|
static alias_var andersen_add_var_same (struct tree_alias_ops *,
|
|
tree, alias_var);
|
|
static bool pointer_destroying_op (tree);
|
|
static aterm_list get_ptset (alias_var);
|
|
static splay_tree ptamap;
|
|
|
|
|
|
static struct tree_alias_ops andersen_ops = {
|
|
andersen_init,
|
|
andersen_cleanup,
|
|
andersen_add_var,
|
|
andersen_add_var_same,
|
|
andersen_simple_assign,
|
|
andersen_addr_assign,
|
|
andersen_ptr_assign,
|
|
andersen_op_assign,
|
|
andersen_heap_assign,
|
|
andersen_assign_ptr,
|
|
andersen_function_def,
|
|
andersen_function_call,
|
|
andersen_may_alias,
|
|
andersen_same_points_to_set,
|
|
andersen_empty_points_to_set,
|
|
0, /* data */
|
|
0, /* Currently non-interprocedural */
|
|
1 /* Can do IP on all statics without help. */
|
|
};
|
|
struct tree_alias_ops *andersen_alias_ops = &andersen_ops;
|
|
|
|
static void term_inclusion (aterm, aterm);
|
|
static void pta_init (void);
|
|
static void pta_reset (void);
|
|
static aterm get_ref (aterm);
|
|
static argterm fun_rec_aterm (aterm_list);
|
|
static aterm pta_make_lam (const char *, aterm, aterm_list);
|
|
static aterm pta_make_ref (const char *);
|
|
static aterm pta_bottom (void);
|
|
static aterm pta_join (aterm, aterm);
|
|
static aterm pta_deref (aterm);
|
|
static aterm pta_rvalue (aterm);
|
|
static aterm pta_address (aterm);
|
|
static void pta_assignment (aterm, aterm);
|
|
static aterm pta_make_fun (const char *, aterm, aterm_list);
|
|
static aterm pta_application (aterm, aterm_list);
|
|
|
|
typedef aterm contents_type;
|
|
static contents_type pta_get_contents (aterm);
|
|
static void pr_ptset_aterm_elem (aterm);
|
|
static void pta_pr_ptset (contents_type);
|
|
|
|
/* Hook for debugging. This function is called instead of
|
|
aterm_inclusion, and lets us print the actual constraints as they
|
|
are generated. */
|
|
|
|
static void
|
|
term_inclusion (aterm t1, aterm t2)
|
|
{
|
|
if (dump_file)
|
|
{
|
|
fprintf (dump_file, "Constraint: ");
|
|
aterm_print (dump_file, t1);
|
|
fprintf (dump_file, " <= ");
|
|
aterm_print (dump_file, t2);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
|
|
aterm_inclusion (t1, t2);
|
|
}
|
|
|
|
/* Initialize libbanshee's constraint engine. */
|
|
|
|
static void
|
|
pta_init (void)
|
|
{
|
|
andersen_terms_init ();
|
|
}
|
|
|
|
/* Reset libbanshee's constraint engine. We do this when we are done
|
|
using it, as it releases the memory libbanshee is using. */
|
|
|
|
static void
|
|
pta_reset (void)
|
|
{
|
|
andersen_terms_reset ();
|
|
}
|
|
|
|
static aterm
|
|
get_ref (aterm t)
|
|
{
|
|
struct ref_decon r_decon;
|
|
r_decon = ref_decon (t);
|
|
|
|
assert (r_decon.f1);
|
|
|
|
return r_decon.f1;
|
|
}
|
|
|
|
/* Make a function record out of the arguments. */
|
|
|
|
static argterm
|
|
fun_rec_aterm (aterm_list args)
|
|
{
|
|
region scratch;
|
|
int counter = 0;
|
|
argterm rest, result;
|
|
aterm_list_scanner scan;
|
|
aterm temp;
|
|
char field_name[512];
|
|
argterm_map map;
|
|
|
|
scratch = newregion ();
|
|
map = new_argterm_map (scratch);
|
|
aterm_list_scan (args, &scan);
|
|
while (aterm_list_next (&scan, &temp))
|
|
{
|
|
snprintf (field_name, 512, "%d", counter++);
|
|
argterm_map_cons (argterm_make_field (field_name, temp), map);
|
|
}
|
|
|
|
rest = argterm_wild ();
|
|
/* rest = argterm_fresh(); */
|
|
|
|
/* safe since field_add makes a copy of the string*/
|
|
result = argterm_row (map, rest);
|
|
|
|
deleteregion (scratch);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static aterm
|
|
pta_make_lam (const char *id, aterm ret, aterm_list args)
|
|
{
|
|
return lam (label_term_constant (id), fun_rec_aterm (args), ret);
|
|
}
|
|
|
|
/* Make a label reference to the given id. */
|
|
|
|
static aterm
|
|
pta_make_ref (const char *id)
|
|
{
|
|
|
|
aterm var = aterm_fresh (id);
|
|
|
|
label_term tag = label_term_constant (id);
|
|
|
|
return ref (tag, var, var);
|
|
}
|
|
|
|
/* Return the empty set. */
|
|
|
|
static aterm
|
|
pta_bottom (void)
|
|
{
|
|
return aterm_zero ();
|
|
}
|
|
|
|
/* Join two terms, such that anything in set t1 will also be in set
|
|
t2, and vice versa. */
|
|
|
|
static aterm
|
|
pta_join (aterm t1, aterm t2)
|
|
{
|
|
aterm result;
|
|
region scratch_rgn = newregion ();
|
|
aterm_list list = new_aterm_list (scratch_rgn);
|
|
|
|
aterm_list_cons (t1, list);
|
|
aterm_list_cons (t2, list);
|
|
|
|
|
|
result = aterm_union (list);
|
|
deleteregion (scratch_rgn);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Generate the constraint for a dereference of term t1. */
|
|
|
|
static aterm
|
|
pta_deref (aterm t1)
|
|
{
|
|
return ref_proj2 (t1);
|
|
}
|
|
|
|
/* Generate the constraint for t1 being an rvalue. */
|
|
|
|
static aterm
|
|
pta_rvalue (aterm t1)
|
|
{
|
|
return pta_deref (t1);
|
|
}
|
|
|
|
/* Generate the constraint for taking the address of t1. */
|
|
|
|
static aterm
|
|
pta_address (aterm t1)
|
|
{
|
|
return ref (label_term_one (), aterm_one (), t1);
|
|
}
|
|
|
|
/* Generate the constraint for assigning t2 to t1. */
|
|
|
|
static void
|
|
pta_assignment (aterm t1, aterm t2)
|
|
{
|
|
term_inclusion (t1, ref_pat1 (t2));
|
|
}
|
|
|
|
/* Make a function from the given name, return value, and arguments. */
|
|
|
|
static aterm
|
|
pta_make_fun (const char *name, aterm ret, aterm_list args)
|
|
{
|
|
aterm temp;
|
|
aterm_list_scanner scan;
|
|
region scratch_rgn = newregion ();
|
|
aterm_list arg_list = new_aterm_list (scratch_rgn);
|
|
|
|
aterm_list_scan (args, &scan);
|
|
|
|
while (aterm_list_next (&scan, &temp))
|
|
{
|
|
aterm_list_cons (get_ref (temp), arg_list);
|
|
}
|
|
|
|
return pta_make_lam (name, get_ref (ret), arg_list);
|
|
}
|
|
|
|
/* Return the constraint for calling function T with arguments
|
|
ACTUALS. */
|
|
|
|
static aterm
|
|
pta_application (aterm t, aterm_list actuals)
|
|
{
|
|
argterm args = fun_rec_aterm (actuals);
|
|
|
|
term_inclusion (t, lam_pat1 (args));
|
|
return pta_address (lam_proj2 (t));
|
|
}
|
|
|
|
/* Return the contents of set expression T. */
|
|
|
|
static contents_type
|
|
pta_get_contents (aterm t)
|
|
{
|
|
struct ref_decon t_decon;
|
|
t_decon = ref_decon (t);
|
|
|
|
return t_decon.f1;
|
|
}
|
|
|
|
/* Print out a points-to set element. */
|
|
|
|
static void
|
|
pr_ptset_aterm_elem (aterm t)
|
|
{
|
|
struct ref_decon ref;
|
|
struct lam_decon lam;
|
|
|
|
ref = ref_decon (t);
|
|
lam = lam_decon (t);
|
|
|
|
fprintf (dump_file, ",");
|
|
if (ref.f0)
|
|
label_term_print (dump_file, ref.f0);
|
|
else if (lam.f0)
|
|
label_term_print (dump_file, lam.f0);
|
|
}
|
|
|
|
|
|
/* Print out a points-to set. */
|
|
|
|
static void
|
|
pta_pr_ptset (contents_type t)
|
|
{
|
|
int size;
|
|
region scratch_rgn;
|
|
aterm_list ptset;
|
|
scratch_rgn = newregion ();
|
|
ptset = aterm_list_copy (scratch_rgn, aterm_tlb (t));
|
|
|
|
size = aterm_list_length (ptset);
|
|
|
|
fprintf (dump_file, "{");
|
|
if (!aterm_list_empty (ptset))
|
|
{
|
|
struct ref_decon ref;
|
|
struct lam_decon lam;
|
|
ref = ref_decon (aterm_list_head (ptset));
|
|
lam = lam_decon (aterm_list_head (ptset));
|
|
if (ref.f0)
|
|
label_term_print (dump_file, ref.f0);
|
|
else if (lam.f0)
|
|
label_term_print (dump_file, lam.f0);
|
|
|
|
/* aterm_pr(stdout,aterm_hd(ptset)); */
|
|
ptset = aterm_list_tail (ptset);
|
|
}
|
|
aterm_list_app (ptset, pr_ptset_aterm_elem);
|
|
fprintf (dump_file, "}(%d)\n", size);
|
|
deleteregion (scratch_rgn);
|
|
}
|
|
|
|
/* Initialize Andersen alias analysis. */
|
|
static int initted = 0;
|
|
|
|
static void
|
|
andersen_init (struct tree_alias_ops *ops ATTRIBUTE_UNUSED)
|
|
{
|
|
#if 0
|
|
/* Don't claim we can do ip partial unless we have unit_at_a_time on. */
|
|
if (!flag_unit_at_a_time)
|
|
#endif
|
|
andersen_ops.ip_partial = 0;
|
|
if (!initted || (!andersen_ops.ip_partial && !andersen_ops.ip))
|
|
{
|
|
pta_init ();
|
|
andersen_rgn = newregion ();
|
|
initted = 1;
|
|
}
|
|
|
|
ptamap = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
|
|
|
|
}
|
|
|
|
static int
|
|
print_out_result (splay_tree_node node, void *data ATTRIBUTE_UNUSED)
|
|
{
|
|
fprintf (dump_file, "%s :=",
|
|
alias_get_name (ALIAS_VAR_DECL (((alias_var) node->value))));
|
|
pta_pr_ptset (pta_get_contents ((aterm) node->key));
|
|
return 0;
|
|
}
|
|
|
|
/* Cleanup after Andersen alias analysis. */
|
|
|
|
static void
|
|
andersen_cleanup (struct tree_alias_ops *ops ATTRIBUTE_UNUSED)
|
|
{
|
|
if (dump_file)
|
|
{
|
|
if (dump_flags & TDF_STATS)
|
|
{
|
|
fprintf (dump_file, "\nPoints-to stats:\n");
|
|
andersen_terms_stats (dump_file);
|
|
}
|
|
|
|
fprintf (dump_file, "\nPoints-to sets:\n");
|
|
splay_tree_foreach (ptamap, print_out_result, NULL);
|
|
}
|
|
|
|
splay_tree_delete (ptamap);
|
|
|
|
if (!andersen_ops.ip_partial && !andersen_ops.ip)
|
|
{
|
|
pta_reset ();
|
|
deleteregion (andersen_rgn);
|
|
andersen_rgn = NULL;
|
|
}
|
|
}
|
|
|
|
/* Add decl to the analyzer, and return a var for it. For
|
|
Andersen, we create a new alias var for the declaration, and
|
|
return that. */
|
|
|
|
static alias_var
|
|
andersen_add_var (struct tree_alias_ops *ops ATTRIBUTE_UNUSED, tree decl)
|
|
{
|
|
alias_var ret;
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
fprintf (dump_file, "Adding variable %s\n",
|
|
alias_get_name (decl));
|
|
|
|
if (alias_get_name (decl) != NULL)
|
|
{
|
|
ret = alias_var_new_with_aterm (decl,
|
|
pta_make_ref (alias_get_name (decl)));
|
|
}
|
|
else
|
|
{
|
|
char *tmp_name;
|
|
ASM_FORMAT_PRIVATE_NAME (tmp_name, "unnamed var", id_num++);
|
|
ret = alias_var_new_with_aterm (decl, pta_make_ref (tmp_name));
|
|
}
|
|
splay_tree_insert (ptamap, (splay_tree_key) ALIAS_VAR_ATERM (ret),
|
|
(splay_tree_value) ret);
|
|
ALIAS_VAR_PTSET (ret) = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Add a variable to the analyzer that is equivalent (as far as
|
|
aliases go) to some existing alias variable.
|
|
For Andersen, we just call a function that does this for us. */
|
|
|
|
static alias_var
|
|
andersen_add_var_same (struct tree_alias_ops *ops ATTRIBUTE_UNUSED, tree decl,
|
|
alias_var tv)
|
|
{
|
|
alias_var ret;
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
fprintf (dump_file, "Adding variable %s same as %s\n",
|
|
alias_get_name (decl), alias_get_name (ALIAS_VAR_DECL (tv)));
|
|
|
|
if (alias_get_name (decl) != NULL)
|
|
ret = alias_var_new_with_aterm (decl,
|
|
pta_make_ref (alias_get_name (decl)));
|
|
else
|
|
{
|
|
char *tmp_name;
|
|
ASM_FORMAT_PRIVATE_NAME (tmp_name, "unnamed var", id_num++);
|
|
ret = alias_var_new_with_aterm (decl, pta_make_ref (tmp_name));
|
|
}
|
|
|
|
pta_join (ALIAS_VAR_ATERM (tv), ALIAS_VAR_ATERM (ret));
|
|
splay_tree_insert (ptamap, (splay_tree_key) ALIAS_VAR_ATERM (ret),
|
|
(splay_tree_value) ret);
|
|
ALIAS_VAR_PTSET (tv) = NULL;
|
|
ALIAS_VAR_PTSET (ret) = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Inference for simple assignment (lhs = rhs) */
|
|
|
|
static void
|
|
andersen_simple_assign (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var lhs, alias_var rhs)
|
|
{
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
fprintf (dump_file, "Simple assignment %s = %s\n",
|
|
alias_get_name (ALIAS_VAR_DECL (lhs)),
|
|
alias_get_name (ALIAS_VAR_DECL (rhs)));
|
|
if (lhs == rhs)
|
|
return;
|
|
|
|
/* The rvalue is just the term itself, and we generate a constraint
|
|
for assigning it to the lhs. */
|
|
pta_assignment (ALIAS_VAR_ATERM (lhs),
|
|
pta_rvalue (ALIAS_VAR_ATERM (rhs)));
|
|
}
|
|
|
|
/* Inference for address assignment (lhs = &addr) */
|
|
|
|
static void
|
|
andersen_addr_assign (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var lhs, alias_var addr)
|
|
{
|
|
if (addr == NULL)
|
|
return;
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
fprintf (dump_file, "Address assignment %s = &%s\n",
|
|
alias_get_name (ALIAS_VAR_DECL (lhs)),
|
|
alias_get_name (ALIAS_VAR_DECL (addr)));
|
|
|
|
/* The rvalue here is the address of a term, and we generate a
|
|
constraint to assign this address to the lhs. */
|
|
pta_assignment (ALIAS_VAR_ATERM (lhs),
|
|
pta_rvalue (pta_address (ALIAS_VAR_ATERM (addr))));
|
|
}
|
|
|
|
|
|
/* Inference for pointer assignment (lhs = *ptr) */
|
|
|
|
static void
|
|
andersen_ptr_assign (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var lhs, alias_var ptr)
|
|
{
|
|
|
|
if (ptr == NULL)
|
|
return;
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
fprintf (dump_file, "Pointer assignment %s = *%s\n",
|
|
alias_get_name (ALIAS_VAR_DECL (lhs)),
|
|
alias_get_name (ALIAS_VAR_DECL (ptr)));
|
|
|
|
pta_assignment (ALIAS_VAR_ATERM (lhs),
|
|
pta_rvalue (pta_deref (ALIAS_VAR_ATERM (ptr))));
|
|
|
|
}
|
|
|
|
/* Determine if OP destroys the current assumed to be valid pointer
|
|
(whether it generates a new valid pointer is not relevant). */
|
|
|
|
static bool
|
|
pointer_destroying_op (tree op)
|
|
{
|
|
switch (TREE_CODE (op))
|
|
{
|
|
case TRUTH_AND_EXPR:
|
|
case TRUTH_OR_EXPR:
|
|
case TRUTH_NOT_EXPR:
|
|
case LT_EXPR:
|
|
case GT_EXPR:
|
|
case GE_EXPR:
|
|
case LE_EXPR:
|
|
case EQ_EXPR:
|
|
case NE_EXPR:
|
|
case MULT_EXPR:
|
|
case TRUNC_DIV_EXPR:
|
|
case LSHIFT_EXPR:
|
|
case RSHIFT_EXPR:
|
|
case LROTATE_EXPR:
|
|
case RROTATE_EXPR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Inference rule for operations (lhs = operation(operands)). */
|
|
|
|
static void
|
|
andersen_op_assign (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var lhs, varray_type operands, tree operation,
|
|
bitmap addrargs)
|
|
{
|
|
aterm newvar = NULL;
|
|
|
|
if (VARRAY_ACTIVE_SIZE (operands) == 0)
|
|
return;
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "Op assignment %s = ",
|
|
alias_get_name (ALIAS_VAR_DECL (lhs)));
|
|
print_generic_stmt (dump_file, operation, dump_flags);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
|
|
|
|
/* Pointer destroying operations do not give us the same valid pointer
|
|
back, and thus, are assignment to pta_bottom. */
|
|
if (pointer_destroying_op (operation))
|
|
{
|
|
pta_assignment (ALIAS_VAR_ATERM (lhs), pta_rvalue (pta_bottom ()));
|
|
return;
|
|
}
|
|
|
|
/* Operations in general we can't track the exact effect of. Thus,
|
|
we conservatively assume that it could make the LHS point to
|
|
*anything* the RHS points to. To signify this, we join the RHS
|
|
variables together and assign it to the LHS. */
|
|
/* The >2 case occurs when we are dealing with constructors. */
|
|
if (VARRAY_ACTIVE_SIZE (operands) > 2)
|
|
{
|
|
size_t i;
|
|
alias_var tv1 = VARRAY_GENERIC_PTR (operands, 0);
|
|
newvar = ALIAS_VAR_ATERM (tv1);
|
|
for (i = 1; i < VARRAY_ACTIVE_SIZE (operands); i++)
|
|
{
|
|
alias_var tempvar = VARRAY_GENERIC_PTR (operands, i);
|
|
aterm t2 = ALIAS_VAR_ATERM (tempvar);
|
|
if (bitmap_bit_p (addrargs, i))
|
|
newvar = pta_join (newvar, pta_address (t2));
|
|
else
|
|
newvar = pta_join (newvar, t2);
|
|
}
|
|
}
|
|
else if (VARRAY_ACTIVE_SIZE (operands) == 2)
|
|
{
|
|
alias_var tv1 = VARRAY_GENERIC_PTR (operands, 0);
|
|
alias_var tv2 = VARRAY_GENERIC_PTR (operands, 1);
|
|
aterm t1 = ALIAS_VAR_ATERM (tv1);
|
|
aterm t2 = ALIAS_VAR_ATERM (tv2);
|
|
if (bitmap_bit_p (addrargs, 0) && bitmap_bit_p (addrargs, 1))
|
|
newvar = pta_join (pta_address (t1), pta_address (t2));
|
|
else if (bitmap_bit_p (addrargs, 0))
|
|
newvar = pta_join (pta_address (t1), t2);
|
|
else if (bitmap_bit_p (addrargs, 1))
|
|
newvar = pta_join (t1, pta_address (t2));
|
|
else
|
|
newvar = pta_join (t1, t2);
|
|
}
|
|
else if (VARRAY_ACTIVE_SIZE (operands) == 1)
|
|
{
|
|
alias_var tv1 = VARRAY_GENERIC_PTR (operands, 0);
|
|
aterm t1 = ALIAS_VAR_ATERM (tv1);
|
|
if (bitmap_bit_p (addrargs, 0))
|
|
newvar = pta_address (t1);
|
|
else
|
|
newvar = t1;
|
|
}
|
|
pta_assignment (ALIAS_VAR_ATERM (lhs), pta_rvalue (newvar));
|
|
}
|
|
|
|
/* Inference for heap assignment (lhs = alloc). */
|
|
|
|
static void
|
|
andersen_heap_assign (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var lhs ATTRIBUTE_UNUSED)
|
|
{
|
|
#if 0
|
|
alias_type type1;
|
|
ECR tau;
|
|
type1 = ECR_get_type (alias_var_get_ECR (lhs));
|
|
tau = alias_ltype_loc (type1);
|
|
|
|
if (ECR_get_type (tau) == alias_bottom)
|
|
ECR_set_type (tau, alias_ltype_new ());
|
|
#endif
|
|
}
|
|
|
|
/* Inference for assignment to a pointer (*ptr = rhs). */
|
|
|
|
static void
|
|
andersen_assign_ptr (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var ptr, alias_var rhs)
|
|
{
|
|
|
|
if (rhs == NULL)
|
|
return;
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
fprintf (dump_file, "Assignment to pointer *%s = %s\n",
|
|
alias_get_name (ALIAS_VAR_DECL (ptr)),
|
|
alias_get_name (ALIAS_VAR_DECL (rhs)));
|
|
/* The RHS is a standard rvalue, and the LHS is a pointer
|
|
dereference. */
|
|
pta_assignment (pta_deref (ALIAS_VAR_ATERM (ptr)),
|
|
pta_rvalue (ALIAS_VAR_ATERM (rhs)));
|
|
}
|
|
|
|
/* Inference for a function definition. */
|
|
|
|
static void
|
|
andersen_function_def (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var func, varray_type params,
|
|
alias_var retval)
|
|
{
|
|
aterm_list args = new_aterm_list (andersen_rgn);
|
|
aterm fun_type;
|
|
|
|
size_t l = VARRAY_ACTIVE_SIZE (params);
|
|
size_t i;
|
|
|
|
/* Set up the arguments for the new function type. */
|
|
for (i = 0; i < l; i++)
|
|
{
|
|
alias_var tv = VARRAY_GENERIC_PTR (params, i);
|
|
aterm_list_cons (ALIAS_VAR_ATERM (tv), args);
|
|
}
|
|
/* Create the function type. */
|
|
fun_type = pta_make_fun (alias_get_name (ALIAS_VAR_DECL (func)),
|
|
ALIAS_VAR_ATERM (retval), args);
|
|
|
|
/* Assign the function type itself to the function. */
|
|
pta_assignment (ALIAS_VAR_ATERM (func), fun_type);
|
|
}
|
|
|
|
/* Inference for a function call assignment. */
|
|
|
|
static int
|
|
andersen_function_call (struct tree_alias_ops *ops,
|
|
alias_var lhs, alias_var func,
|
|
varray_type args, bitmap addrargs)
|
|
{
|
|
aterm_list actuals = new_aterm_list (andersen_rgn);
|
|
aterm ftype = ALIAS_VAR_ATERM (func);
|
|
aterm ret = NULL;
|
|
aterm res;
|
|
tree decl = ALIAS_VAR_DECL (func);
|
|
|
|
size_t i;
|
|
|
|
if (lhs)
|
|
ret = ALIAS_VAR_ATERM (lhs);
|
|
for (i = 0; i < VARRAY_ACTIVE_SIZE (args); i++)
|
|
{
|
|
alias_var argtv = VARRAY_GENERIC_PTR (args, i);
|
|
aterm arg = ALIAS_VAR_ATERM (argtv);
|
|
if (bitmap_bit_p (addrargs, i))
|
|
aterm_list_cons (pta_rvalue (pta_address (arg)), actuals);
|
|
else
|
|
aterm_list_cons (pta_rvalue (arg), actuals);
|
|
}
|
|
aterm_list_reverse (actuals);
|
|
|
|
/* Generate the constraint that calls the function with it's
|
|
arguments, and gives us the result. This in turn applies
|
|
whatever constraints are in that function. */
|
|
res = pta_application (pta_rvalue (ftype), actuals);
|
|
/* We only need care about the result if we have an LHS. If we do,
|
|
assign the result of function application back to the LHS. */
|
|
if (ret)
|
|
pta_assignment (ret, pta_rvalue (res));
|
|
|
|
/* We can handle functions we've got trees for. non-statics will
|
|
just have incoming parameters assigned to global_var if
|
|
necessary. */
|
|
if (TREE_CODE (decl) == FUNCTION_DECL
|
|
&& DECL_PTA_ALIASVAR (decl)
|
|
&& ops->ip_partial
|
|
&& (cgraph_local_info (decl)->local))
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Simple pointer comparison function for list sorting. */
|
|
|
|
static int
|
|
simple_cmp (const aterm a, const aterm b)
|
|
{
|
|
return (int *)a - (int *)b;
|
|
}
|
|
|
|
|
|
/* Get the points-to set for TV, caching if it we had to compute it. */
|
|
|
|
static aterm_list
|
|
get_ptset (alias_var tv)
|
|
{
|
|
aterm_list ptset;
|
|
ptset = ALIAS_VAR_PTSET (tv);
|
|
if (ptset != NULL)
|
|
return ptset;
|
|
ptset = aterm_tlb (pta_get_contents (ALIAS_VAR_ATERM (tv)));
|
|
ALIAS_VAR_PTSET (tv) = ptset;
|
|
return ptset;
|
|
}
|
|
|
|
|
|
/* Determine if two aterm's have the same points-to set. */
|
|
|
|
static bool
|
|
andersen_same_points_to_set (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var ptrtv, alias_var vartv)
|
|
{
|
|
aterm_list ptset1, ptset2;
|
|
aterm_list_scanner scan1, scan2;
|
|
aterm data1, data2;
|
|
region scratch_rgn = newregion ();
|
|
|
|
ptset1 = get_ptset (ptrtv);
|
|
ptset2 = get_ptset (vartv);
|
|
|
|
if (aterm_list_length (ptset1) != aterm_list_length (ptset2))
|
|
{
|
|
deleteregion (scratch_rgn);
|
|
return false;
|
|
}
|
|
|
|
if (ptset1 == ptset2)
|
|
{
|
|
deleteregion (scratch_rgn);
|
|
return true;
|
|
}
|
|
|
|
ptset1 = aterm_list_copy (scratch_rgn, ptset1);
|
|
ptset2 = aterm_list_copy (scratch_rgn, ptset2);
|
|
|
|
if (aterm_list_length (ptset1) != aterm_list_length (ptset2))
|
|
{
|
|
deleteregion (scratch_rgn);
|
|
return false;
|
|
}
|
|
|
|
ptset1 = aterm_list_sort (ptset1, simple_cmp);
|
|
ptset2 = aterm_list_sort (ptset2, simple_cmp);
|
|
|
|
aterm_list_scan (ptset1, &scan1);
|
|
aterm_list_scan (ptset2, &scan2);
|
|
while (aterm_list_next (&scan1, &data1))
|
|
{
|
|
aterm_list_next (&scan2, &data2);
|
|
if (data1 != data2)
|
|
{
|
|
deleteregion(scratch_rgn);
|
|
return false;
|
|
}
|
|
}
|
|
deleteregion(scratch_rgn);
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Determine if two variables may alias. In our case, this means
|
|
whether the decl represented by PTRTV can point to VARTV. */
|
|
|
|
static bool
|
|
andersen_may_alias (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var ptrtv, alias_var vartv)
|
|
{
|
|
aterm_list ptset;
|
|
ptset = get_ptset (ptrtv);
|
|
|
|
if (aterm_list_empty (ptset))
|
|
return false;
|
|
|
|
return aterm_list_member (ptset, ALIAS_VAR_ATERM (vartv));
|
|
}
|
|
|
|
/* Determine whether PTRTV has an empty points-to set. IE it may not
|
|
point to anything. */
|
|
|
|
static bool
|
|
andersen_empty_points_to_set (struct tree_alias_ops *ops ATTRIBUTE_UNUSED,
|
|
alias_var ptrtv)
|
|
{
|
|
aterm_list ptset;
|
|
ptset = get_ptset (ptrtv);
|
|
return aterm_list_empty (ptset);
|
|
}
|