tree-alias-ander.c [plain text]
#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"
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,
0,
1
};
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);
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);
}
static void
pta_init (void)
{
andersen_terms_init ();
}
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;
}
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 ();
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);
}
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);
}
static aterm
pta_bottom (void)
{
return aterm_zero ();
}
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;
}
static aterm
pta_deref (aterm t1)
{
return ref_proj2 (t1);
}
static aterm
pta_rvalue (aterm t1)
{
return pta_deref (t1);
}
static aterm
pta_address (aterm t1)
{
return ref (label_term_one (), aterm_one (), t1);
}
static void
pta_assignment (aterm t1, aterm t2)
{
term_inclusion (t1, ref_pat1 (t2));
}
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);
}
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));
}
static contents_type
pta_get_contents (aterm t)
{
struct ref_decon t_decon;
t_decon = ref_decon (t);
return t_decon.f1;
}
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);
}
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);
ptset = aterm_list_tail (ptset);
}
aterm_list_app (ptset, pr_ptset_aterm_elem);
fprintf (dump_file, "}(%d)\n", size);
deleteregion (scratch_rgn);
}
static int initted = 0;
static void
andersen_init (struct tree_alias_ops *ops ATTRIBUTE_UNUSED)
{
#if 0
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;
}
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;
}
}
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;
}
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;
}
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;
pta_assignment (ALIAS_VAR_ATERM (lhs),
pta_rvalue (ALIAS_VAR_ATERM (rhs)));
}
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)));
pta_assignment (ALIAS_VAR_ATERM (lhs),
pta_rvalue (pta_address (ALIAS_VAR_ATERM (addr))));
}
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))));
}
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;
}
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");
}
if (pointer_destroying_op (operation))
{
pta_assignment (ALIAS_VAR_ATERM (lhs), pta_rvalue (pta_bottom ()));
return;
}
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));
}
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
}
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)));
pta_assignment (pta_deref (ALIAS_VAR_ATERM (ptr)),
pta_rvalue (ALIAS_VAR_ATERM (rhs)));
}
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;
for (i = 0; i < l; i++)
{
alias_var tv = VARRAY_GENERIC_PTR (params, i);
aterm_list_cons (ALIAS_VAR_ATERM (tv), args);
}
fun_type = pta_make_fun (alias_get_name (ALIAS_VAR_DECL (func)),
ALIAS_VAR_ATERM (retval), args);
pta_assignment (ALIAS_VAR_ATERM (func), fun_type);
}
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);
res = pta_application (pta_rvalue (ftype), actuals);
if (ret)
pta_assignment (ret, pta_rvalue (res));
if (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_PTA_ALIASVAR (decl)
&& ops->ip_partial
&& (cgraph_local_info (decl)->local))
{
return 0;
}
return 1;
}
static int
simple_cmp (const aterm a, const aterm b)
{
return (int *)a - (int *)b;
}
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;
}
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;
}
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));
}
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);
}