diff options
Diffstat (limited to 'gcc/tree-alias-ander.c')
-rw-r--r-- | gcc/tree-alias-ander.c | 933 |
1 files changed, 933 insertions, 0 deletions
diff --git a/gcc/tree-alias-ander.c b/gcc/tree-alias-ander.c new file mode 100644 index 00000000000..b3b14d1fa12 --- /dev/null +++ b/gcc/tree-alias-ander.c @@ -0,0 +1,933 @@ +/* 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-simple.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); +} |