summaryrefslogtreecommitdiff
path: root/gcc/tree-alias-common.c
diff options
context:
space:
mode:
authordnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-05-13 06:41:07 +0000
committerdnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-05-13 06:41:07 +0000
commit4ee9c6840ad3fc92a9034343278a1e476ad6872a (patch)
treea2568888a519c077427b133de9ece5879a8484a5 /gcc/tree-alias-common.c
parentebb338380ab170c91e64d38038e6b5ce930d69a1 (diff)
downloadgcc-4ee9c6840ad3fc92a9034343278a1e476ad6872a.tar.gz
Merge tree-ssa-20020619-branch into mainline.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@81764 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tree-alias-common.c')
-rw-r--r--gcc/tree-alias-common.c1264
1 files changed, 1264 insertions, 0 deletions
diff --git a/gcc/tree-alias-common.c b/gcc/tree-alias-common.c
new file mode 100644
index 00000000000..cd36eef5942
--- /dev/null
+++ b/gcc/tree-alias-common.c
@@ -0,0 +1,1264 @@
+/* Tree based 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 "tree-alias-type.h"
+#include "bitmap.h"
+#include "tree-alias-common.h"
+/* If we have andersen's points-to analysis, include it. */
+#ifdef HAVE_BANSHEE
+#include "tree-alias-ander.h"
+#endif
+#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 "c-common.h"
+#include "tree-flow.h"
+#include "tree-inline.h"
+#include "varray.h"
+#include "c-tree.h"
+#include "tree-simple.h"
+#include "hashtab.h"
+#include "function.h"
+#include "cgraph.h"
+#include "tree-pass.h"
+#include "timevar.h"
+
+/* Reduce ifdefery later. */
+#ifndef HAVE_BANSHEE
+#define HAVE_BANSHEE 0
+#endif
+
+/* This file contains the implementation of the common parts of the
+ tree points-to analysis infrastructure.
+
+ Overview:
+
+ This file contains the points-to analysis driver. It does two main things:
+ 1. Keeps track of the PTA data for each variable (IE the data each
+ specific PTA implementation wants to keep associated with a
+ variable).
+ 2. Walks the function trees, calling the appropriate functions that
+ each PTA implementation has implemented.
+
+ In order to speed up PTA queries, the PTA specific data is stored
+ in the tree for *_DECL's, in DECL_PTA_ALIASVAR. This way, we only
+ need to use the hash table for non-DECL's. */
+#define FIELD_BASED 0
+
+/* Array of all created alias_vars.
+ Note that this should contain all the alias_vars we wanted
+ marked during GC. */
+static GTY((param_is (union alias_var_def))) varray_type alias_vars = NULL;
+struct tree_alias_ops *current_alias_ops;
+
+/* Array of local (to a function) alias_vars.
+ Note that this should contain all the alias_vars that are
+ local to this function. We delete these from alias_vars before
+ collection. */
+static GTY(()) varray_type local_alias_vars;
+static GTY(()) varray_type local_alias_varnums;
+tree pta_global_var;
+static bitmap addrargs;
+static alias_var get_alias_var_decl (tree);
+static alias_var get_alias_var (tree);
+static void find_func_aliases (tree);
+static void deal_with_call_aliasing (tree, alias_var);
+static alias_var create_fun_alias_var_ptf (tree, tree);
+static alias_var create_fun_alias_var (tree, int);
+static alias_var create_alias_var (tree);
+static void intra_function_call (varray_type);
+static void get_values_from_constructor (tree, varray_type *, bitmap, int *);
+static bool call_may_clobber (tree);
+static bool call_may_return (tree);
+
+/* Return true if a EXPR, which is a CALL_EXPR, may clobber variables. */
+
+static bool
+call_may_clobber (tree expr)
+{
+ int flags;
+
+ if (TREE_CODE (expr) != CALL_EXPR)
+ return false;
+
+ flags = call_expr_flags (expr);
+ return (! (flags & (ECF_CONST | ECF_PURE | ECF_NORETURN)));
+}
+
+/* Return true if a EXPR, which is a CALL_EXPR, may return. */
+
+static bool
+call_may_return (tree expr)
+{
+ int flags;
+
+ if (TREE_CODE (expr) != CALL_EXPR)
+ return false;
+
+ flags = call_expr_flags (expr);
+ return ! (flags & ECF_NORETURN);
+}
+
+/* Get the alias_var for DECL.
+ Creates the alias_var if it does not exist already. Also
+ handles FUNCTION_DECL properly. */
+
+static alias_var
+get_alias_var_decl (tree decl)
+{
+ alias_var newvar;
+ if (TREE_CODE (decl) == FIELD_DECL)
+ abort ();
+ if (DECL_P (decl))
+ {
+ if (DECL_PTA_ALIASVAR (decl))
+ return DECL_PTA_ALIASVAR (decl);
+ }
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ newvar = create_fun_alias_var (decl, 0);
+ else
+ {
+ newvar = create_alias_var (decl);
+ /* Assign globals to global var for purposes of intraprocedural
+ analysis. */
+ if ((DECL_CONTEXT (decl) == NULL
+ || TREE_PUBLIC (decl)
+ || TREE_STATIC (decl)
+ || decl_function_context (decl) == NULL)
+ && decl != pta_global_var)
+ {
+ current_alias_ops->addr_assign (current_alias_ops,
+ get_alias_var (pta_global_var),
+ newvar);
+ /* If the global has some DECL_INITIAL, we need to process
+ it here. */
+ if (DECL_INITIAL (decl))
+ find_func_aliases (decl);
+ }
+ }
+
+ if (!current_alias_ops->ip)
+ {
+ if (!current_alias_ops->ip_partial
+ || (TREE_CODE (decl) != FUNCTION_DECL
+ && TREE_CODE (decl) != PARM_DECL))
+ {
+ VARRAY_PUSH_INT (local_alias_varnums, ALIAS_VAR_VARNUM (newvar));
+ VARRAY_PUSH_TREE (local_alias_vars, decl);
+ }
+ }
+ return newvar;
+}
+
+/* Get the alias_var for an expression EXPR.
+ Note that this function expects to only be handed a RHS or LHS, not
+ a MODIFY_EXPR. */
+
+static alias_var
+get_alias_var (tree expr)
+{
+ /* If it's a decl, get the alias var of the decl. We farm this off
+ to get_alias_var_decl so it can abort if the alias var doesn't
+ exist, and in case something else *knows* it has a decl, and
+ wants the alias var. */
+
+ if (DECL_P (expr))
+ return get_alias_var_decl (expr);
+
+ /* True constants have no aliases (unless modifiable strings are on,
+ in which case i don't think we'll end up with a STRING_CST anyway) */
+ if (TREE_CODE_CLASS (TREE_CODE (expr)) == 'c')
+ return NULL;
+
+
+ switch (TREE_CODE (expr))
+ {
+ case ARRAY_REF:
+ {
+ /* Find the first non-array ref, and return it's alias
+ variable */
+ tree p;
+ for (p = expr; TREE_CODE (p) == ARRAY_REF;
+ p = TREE_OPERAND (p, 0));
+ return get_alias_var (p);
+ }
+ break;
+ case COMPONENT_REF:
+ {
+#if FIELD_BASED
+ bool safe = true;
+ tree p;
+ for (p = expr;
+ TREE_CODE (p) == COMPONENT_REF || TREE_CODE (p) == INDIRECT_REF;
+ p = TREE_OPERAND (p, 0))
+ {
+ if (TREE_CODE (TREE_TYPE (p)) == UNION_TYPE
+ || TREE_CODE (TREE_TYPE (p)) == QUAL_UNION_TYPE)
+ {
+ safe = false;
+ break;
+ }
+ }
+ if (!safe)
+ {
+ for (p = expr; TREE_CODE (p) == COMPONENT_REF;
+ p = TREE_OPERAND (p, 0));
+ return get_alias_var (p);
+ }
+ else
+ {
+ return get_alias_var (TREE_OPERAND (expr, 1));
+ }
+#else
+ /* Find the first non-component ref, and return its alias variable. */
+ tree p;
+ for (p = expr; TREE_CODE (p) == COMPONENT_REF;
+ p = TREE_OPERAND (p, 0));
+ return get_alias_var (p);
+#endif
+ }
+ break;
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_CEIL_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case ADDR_EXPR:
+ case INDIRECT_REF:
+ case BIT_FIELD_REF:
+ /* If it's a ref or cast or conversion of something, get the
+ alias var of the something. */
+ return get_alias_var (TREE_OPERAND (expr, 0));
+ break;
+
+ default:
+ return NULL;
+ }
+}
+
+/* Perform conservative aliasing for an intraprocedural mode function
+ call. ARGS are the arguments that were passed to that function
+ call. */
+
+static void
+intra_function_call (varray_type args)
+{
+ size_t l = VARRAY_ACTIVE_SIZE (args);
+ size_t i;
+ alias_var av = get_alias_var (pta_global_var);
+
+ /* We assume assignments among the actual parameters. */
+ for (i = 0; i < l; i++)
+ {
+ alias_var argi = VARRAY_GENERIC_PTR (args, i);
+ size_t j;
+ for (j = 0; j < l; j++)
+ {
+ alias_var argj;
+ if (i == j)
+ continue;
+ argj = VARRAY_GENERIC_PTR (args, j);
+ /* Restricted pointers can't be aliased with other
+ restricted pointers. */
+ if (!TYPE_RESTRICT (TREE_TYPE (ALIAS_VAR_DECL (argi)))
+ || !TYPE_RESTRICT (TREE_TYPE (ALIAS_VAR_DECL (argj))))
+ /* Do a bit of TBAA to avoid pointless assignments. */
+ if (alias_sets_conflict_p (get_alias_set (ALIAS_VAR_DECL (argi)),
+ get_alias_set (ALIAS_VAR_DECL (argj))))
+ current_alias_ops->simple_assign (current_alias_ops, argi, argj);
+ }
+ }
+ /* We assume that an actual parameter can point to any global. */
+ for (i = 0; i < l; i++)
+ {
+ alias_var argav = VARRAY_GENERIC_PTR (args, i);
+ /* Restricted pointers can't be aliased with other
+ restricted pointers. */
+ if (!TYPE_RESTRICT (TREE_TYPE (ALIAS_VAR_DECL (argav)))
+ || !TYPE_RESTRICT (TREE_TYPE (ALIAS_VAR_DECL (av))))
+ {
+ /* Arguments can alias globals, and whatever they point to
+ can point to a global as well. */
+ current_alias_ops->simple_assign (current_alias_ops, argav, av);
+ }
+ }
+}
+
+/* Put all pointers in a constructor in an array. */
+
+static void
+get_values_from_constructor (tree constructor, varray_type *vals,
+ bitmap addrargs, int *i)
+{
+ tree elt_list;
+ switch (TREE_CODE (constructor))
+ {
+ case CONSTRUCTOR:
+ {
+ for (elt_list = CONSTRUCTOR_ELTS (constructor);
+ elt_list;
+ elt_list = TREE_CHAIN (elt_list))
+ {
+ tree value = TREE_VALUE (elt_list);
+ if (TREE_CODE (value) == TREE_LIST
+ || TREE_CODE (value) == CONSTRUCTOR)
+ {
+ get_values_from_constructor (value, vals, addrargs, i); }
+ else
+ {
+ alias_var aav;
+ aav = get_alias_var (value);
+ if (aav)
+ VARRAY_PUSH_GENERIC_PTR (*vals, aav);
+ if (TREE_CODE (value) == ADDR_EXPR)
+ bitmap_set_bit (addrargs, *i);
+ *i = *i + 1;
+ }
+ }
+ }
+ break;
+ case TREE_LIST:
+ for (elt_list = constructor;
+ elt_list;
+ elt_list = TREE_CHAIN (elt_list))
+ {
+ get_values_from_constructor (TREE_VALUE (elt_list), vals, addrargs, i);
+ }
+ break;
+ default:
+ abort();
+ }
+}
+
+/* Deal with the possible return values of a call that we don't have
+ actual PTA info about. */
+
+static void
+deal_with_call_aliasing (tree callargs, alias_var lhsAV)
+{
+ tree arg, argp;
+
+ for (argp = callargs;
+ argp;
+ argp = TREE_CHAIN (argp))
+ {
+ arg = TREE_VALUE (argp);
+ /* If we take the address of a variable directly in the
+ argument, the return value could be the address of that
+ variable. */
+ if (TREE_CODE (arg) == ADDR_EXPR)
+ current_alias_ops->addr_assign (current_alias_ops, lhsAV,
+ get_alias_var (arg));
+ /* If we pass in a pointer, we could return that pointer. */
+ else if (POINTER_TYPE_P (TREE_TYPE (arg)))
+ {
+ alias_var argtv = get_alias_var (arg);
+ if (argtv)
+ current_alias_ops->simple_assign (current_alias_ops, lhsAV,
+ argtv);
+ }
+ }
+}
+
+/* Find the operand of the component ref that actually is doing
+ something to the DECL */
+static tree
+find_op_of_decl (tree cref)
+{
+ while (!DECL_P (TREE_OPERAND (cref, 0)))
+ {
+ cref = TREE_OPERAND (cref, 0);
+ }
+ return cref;
+}
+
+
+/* Tree walker that is the heart of the aliasing infrastructure.
+ TP is a pointer to the current tree.
+ WALK_SUBTREES specifies whether to continue traversing subtrees or
+ not.
+ Returns NULL_TREE when we should stop.
+
+ This function is the main part of the aliasing infrastructure. It
+ walks the trees, calling the appropriate alias analyzer functions to process
+ various statements. */
+
+static void
+find_func_aliases (tree stp)
+{
+ if (TREE_CODE (stp) == RETURN_EXPR)
+ {
+ stp = TREE_OPERAND (stp, 0);
+ if (!stp)
+ return;
+ }
+
+ if (TREE_CODE (stp) == MODIFY_EXPR
+ || (DECL_P (stp) && DECL_INITIAL (stp) != NULL_TREE ))
+ {
+ tree op0, op1;
+ alias_var lhsAV = NULL;
+ alias_var rhsAV = NULL;
+
+ if (DECL_P (stp))
+ {
+ op0 = stp;
+ op1 = DECL_INITIAL (stp);
+ }
+ else
+ {
+ op0 = TREE_OPERAND (stp, 0);
+ op1 = TREE_OPERAND (stp, 1);
+ }
+ /* lhsAV should always have an alias variable */
+ lhsAV = get_alias_var (op0);
+ if (!lhsAV)
+ return;
+ /* rhsAV might not have one, c.f. c = 5 */
+ rhsAV = get_alias_var (op1);
+#if !FIELD_BASED
+ while (TREE_CODE (op1) == COMPONENT_REF
+ && TREE_CODE (TREE_OPERAND (op1, 0)) == COMPONENT_REF)
+ {
+ op1 = TREE_OPERAND (op1, 0);
+ }
+ while (TREE_CODE (op1) == BIT_FIELD_REF)
+ {
+ op1 = TREE_OPERAND (op1, 0);
+ }
+ /* Take care of fact that we may have multi-level component
+ refs. */
+ if (TREE_CODE (op1) == COMPONENT_REF)
+ op1 = find_op_of_decl (op1);
+#endif
+
+ /* You would think we could test rhsAV at the top, rather than
+ 50 separate times, but we can't, because it can be NULL for
+ operator assignments, where we'd still collect the individual
+ alias vars for the operator. */
+
+ /* Note that structures are treated as a single alias
+ variable, since we can disambiguate based on TBAA first,
+ and fall back on points-to. */
+ /* x = <something> */
+ if (is_gimple_variable (op0))
+ {
+ /* x = y */
+ if (is_gimple_variable (op1))
+ {
+ if (rhsAV != NULL)
+ current_alias_ops->simple_assign (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ /* x = foo.y */
+ else if (TREE_CODE (op1) == COMPONENT_REF
+ && DECL_P (TREE_OPERAND (op1, 0)))
+ {
+ if (rhsAV != NULL)
+ current_alias_ops->simple_assign (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ /* x = (cast) [maybe-addr-expr] y */
+ else if (is_gimple_cast (op1))
+ {
+ tree stripped_op1 = op1;
+ STRIP_NOPS (stripped_op1);
+ if (rhsAV != NULL)
+ {
+ if (TREE_CODE (stripped_op1) == ADDR_EXPR)
+ current_alias_ops->addr_assign (current_alias_ops, lhsAV,
+ rhsAV);
+ else
+ current_alias_ops->simple_assign (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ }
+ /* x = *y or x = foo->y */
+ else if (TREE_CODE (op1) == INDIRECT_REF
+ || TREE_CODE (op1) == ARRAY_REF
+ || (TREE_CODE (op1) == COMPONENT_REF
+ && TREE_CODE (TREE_OPERAND (op1, 0)) == INDIRECT_REF))
+ {
+ if (rhsAV != NULL)
+ current_alias_ops->ptr_assign (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ /* x = &y = x = &foo.y */
+ else if (TREE_CODE (op1) == ADDR_EXPR)
+ {
+ if (rhsAV != NULL)
+ current_alias_ops->addr_assign (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ /* x = func(...) */
+ else if (TREE_CODE (op1) == CALL_EXPR)
+ {
+ /* Heap assignment. These are __attribute__ malloc or
+ something, i'll deal with it later. */
+ if (0)
+ {}
+ else
+ {
+
+ /* NORETURN functions have no effect on aliasing. */
+ if (call_may_return (op1))
+ {
+ varray_type args;
+ tree arg;
+ tree callop0, callop1;
+ int argnum;
+
+ /* Collect the arguments */
+ VARRAY_GENERIC_PTR_INIT (args, 1, "Arguments");
+ bitmap_clear (addrargs);
+ callop1 = TREE_OPERAND (op1, 1);
+ callop0 = TREE_OPERAND (op1, 0);
+ for (arg = callop1, argnum = 0;
+ arg;
+ arg = TREE_CHAIN (arg), argnum++)
+ {
+ alias_var aav = get_alias_var (TREE_VALUE (arg));
+ if (aav)
+ {
+ VARRAY_PUSH_GENERIC_PTR (args, aav);
+ if (TREE_CODE (TREE_VALUE (arg)) == ADDR_EXPR)
+ bitmap_set_bit (addrargs, argnum);
+ }
+ }
+ /* Simulate the call */
+ if (current_alias_ops->function_call (current_alias_ops, lhsAV,
+ get_alias_var (callop0),
+ args, addrargs))
+ {
+ if (call_may_clobber (op1)
+ && !current_alias_ops->ip
+ && flag_argument_noalias != 2)
+ {
+ intra_function_call (args);
+ }
+ if (POINTER_TYPE_P (TREE_TYPE (op0)))
+ deal_with_call_aliasing (callop1, lhsAV);
+ }
+ }
+ }
+ }
+ /* x = op (...) */
+ else
+ {
+ bitmap_clear (addrargs);
+ if (TREE_CODE (op1) == CONSTRUCTOR)
+ {
+ varray_type ops;
+ int i = 0;
+ VARRAY_GENERIC_PTR_INIT (ops, 1, "Operands");
+ get_values_from_constructor (op1, &ops, addrargs, &i);
+ current_alias_ops->op_assign (current_alias_ops, lhsAV,
+ ops, op1, addrargs);
+ }
+ else
+ switch (TREE_CODE_CLASS (TREE_CODE (op1)))
+ {
+ case 'e': /* an expression */
+ case 's': /* an expression with side effects */
+ case '<': /* a comparison expression */
+ case '1': /* a unary arithmetic expression */
+ case 'r': /* a reference */
+ case '2': /* a binary arithmetic expression */
+ {
+ tree op;
+ varray_type ops;
+ int i;
+ VARRAY_GENERIC_PTR_INIT (ops, 1, "Operands");
+ for (i = 0; i < TREE_CODE_LENGTH (TREE_CODE (op1)); i++)
+ {
+ alias_var aav;
+ op = TREE_OPERAND (op1, i);
+ aav = get_alias_var (op);
+ if (aav)
+ VARRAY_PUSH_GENERIC_PTR (ops, aav);
+ if (TREE_CODE (op) == ADDR_EXPR)
+ bitmap_set_bit (addrargs, i);
+ }
+ current_alias_ops->op_assign (current_alias_ops, lhsAV,
+ ops, op1, addrargs);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ /* *x = <something> */
+ else
+ {
+ /* x.f = y or x->f = y */
+ if ((TREE_CODE (op0) == COMPONENT_REF
+ || TREE_CODE (op0) == BIT_FIELD_REF)
+ && is_gimple_variable (op1))
+ {
+ if (rhsAV != NULL)
+ current_alias_ops->simple_assign (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ /* x.f = &y or x->f = &y */
+ else if (TREE_CODE (op0) == COMPONENT_REF
+ && TREE_CODE (op1) == ADDR_EXPR)
+ {
+ if (rhsAV != NULL)
+ current_alias_ops->addr_assign (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ /* *x.f = y or *x->f = y */
+ else if ((TREE_CODE (op0) == INDIRECT_REF
+ || TREE_CODE (op0) == ARRAY_REF)
+ && TREE_CODE (TREE_OPERAND (op0, 0)) == COMPONENT_REF
+ && is_gimple_variable (op1))
+ {
+ if (rhsAV != NULL)
+ current_alias_ops->assign_ptr (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ /* *x = &y */
+ else if ((TREE_CODE (op0) == INDIRECT_REF
+ || TREE_CODE (op0) == ARRAY_REF)
+ && TREE_CODE (op1) == ADDR_EXPR)
+ {
+ /* This becomes temp = &y and *x = temp . */
+ alias_var tempvar;
+ tree temp = create_tmp_var_raw (void_type_node, "aliastmp");
+ tempvar = current_alias_ops->add_var (current_alias_ops, temp);
+ current_alias_ops->addr_assign (current_alias_ops, tempvar,
+ rhsAV);
+ current_alias_ops->assign_ptr (current_alias_ops, lhsAV,
+ tempvar);
+ }
+
+ /* *x = *y */
+ else if ((TREE_CODE (op0) == INDIRECT_REF
+ || TREE_CODE (op0) == ARRAY_REF)
+ && (TREE_CODE (op1) == INDIRECT_REF
+ || TREE_CODE (op1) == ARRAY_REF))
+ {
+ /* This becomes temp = *y and *x = temp . */
+ alias_var tempvar;
+ tree temp;
+ temp = create_tmp_var_raw (void_type_node, "aliastmp");
+ tempvar = current_alias_ops->add_var (current_alias_ops, temp);
+ current_alias_ops->ptr_assign (current_alias_ops, tempvar,
+ rhsAV);
+ current_alias_ops->assign_ptr (current_alias_ops, lhsAV,
+ tempvar);
+ }
+
+ /* *x = (cast) y */
+ else if ((TREE_CODE (op0) == INDIRECT_REF
+ || TREE_CODE (op0) == ARRAY_REF)
+ && is_gimple_cast (op1))
+ {
+ if (rhsAV != NULL)
+ {
+ /* This becomes temp = (cast) y and *x = temp. */
+ alias_var tempvar;
+ tree temp;
+ temp = create_tmp_var_raw (void_type_node, "aliastmp");
+ tempvar = current_alias_ops->add_var (current_alias_ops,
+ temp);
+ current_alias_ops->simple_assign (current_alias_ops,
+ tempvar, rhsAV);
+ current_alias_ops->assign_ptr (current_alias_ops, lhsAV,
+ tempvar);
+ }
+ }
+ /* *x = <something else> */
+ else
+ {
+ if (rhsAV != NULL)
+ current_alias_ops->assign_ptr (current_alias_ops, lhsAV,
+ rhsAV);
+ }
+ }
+ }
+ /* Calls without return values. */
+ else if (TREE_CODE (stp) == CALL_EXPR)
+ {
+ alias_var callvar;
+ varray_type args;
+ tree arg;
+ callvar = get_alias_var (TREE_OPERAND (stp, 0));
+ if (callvar != NULL)
+ {
+
+ /* NORETURN and CONST functions with no return value
+ have no effect on aliasing (as may be seen above,
+ const functions that return a value might have an
+ effect on aliasing, since the return value can point
+ to one of the arguments. */
+ if (call_may_clobber (stp))
+ {
+ int argnum;
+ VARRAY_GENERIC_PTR_INIT (args, 1, "Arguments");
+ bitmap_clear (addrargs);
+ for (arg = TREE_OPERAND (stp, 1), argnum=0;
+ arg;
+ arg = TREE_CHAIN (arg), argnum++)
+ {
+ alias_var aav = get_alias_var (TREE_VALUE (arg));
+ if (aav)
+ {
+ VARRAY_PUSH_GENERIC_PTR (args, aav);
+ if (TREE_CODE (TREE_VALUE (arg)) == ADDR_EXPR)
+ bitmap_set_bit (addrargs, argnum);
+ }
+
+ }
+
+ if (current_alias_ops->function_call (current_alias_ops, NULL,
+ callvar, args, addrargs))
+ if (!current_alias_ops->ip && flag_argument_noalias != 2)
+ intra_function_call (args);
+ }
+ }
+ }
+}
+
+/* Create the alias_var for a function definition DECL, it's
+ arguments, and it's return value. If FORCE is true, we force
+ creation of the alias_var, regardless of whether one exists already.
+
+ This includes creation of alias_var's for
+ - The function itself.
+ - The arguments.
+ - The return value. */
+
+static alias_var
+create_fun_alias_var (tree decl, int force)
+{
+ alias_var avar, retvar;
+ tree rdecl;
+ varray_type params = NULL;
+
+ if (!force)
+ {
+ if (DECL_PTA_ALIASVAR (decl))
+ return DECL_PTA_ALIASVAR (decl);
+ }
+
+ VARRAY_GENERIC_PTR_INIT (params, 1, "Arguments");
+ if (DECL_ARGUMENTS (decl) != NULL)
+ {
+ tree arg;
+ for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
+ {
+ alias_var var = get_alias_var (arg);
+ VARRAY_PUSH_GENERIC_PTR (params, var);
+ /* Incoming pointers can point to pta_global_var, unless
+ either we are interprocedural, or we can do ip on all
+ statics + this function has been defined + it's not an
+ external function. */
+ if (POINTER_TYPE_P (TREE_TYPE (arg))
+ && !current_alias_ops->ip
+ /* FIXME: Need to let analyzer decide in partial case. */
+ && (!current_alias_ops->ip_partial
+ || !cgraph_local_info (decl)->local))
+ current_alias_ops->simple_assign (current_alias_ops, var,
+ get_alias_var (pta_global_var));
+ }
+ }
+ else if (TYPE_ARG_TYPES (TREE_TYPE (decl)) != NULL)
+ {
+ tree arg;
+ /* FIXME: Handle varargs */
+ for (arg = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ arg && TREE_VALUE (arg) != void_type_node;
+ arg = TREE_CHAIN (arg))
+ {
+ tree fakedecl = create_tmp_var_raw (TREE_VALUE (arg), "normarg");
+ alias_var var;
+ DECL_CONTEXT (fakedecl) = current_function_decl;
+ var = get_alias_var (fakedecl);
+ VARRAY_PUSH_GENERIC_PTR (params, var);
+
+ /* Incoming pointers can point to pta_global_var, unless
+ either we are interprocedural, or we can do ip on all
+ statics + this function has been defined + it's not an
+ external function. */
+ if (POINTER_TYPE_P (TREE_TYPE (fakedecl))
+ && !current_alias_ops->ip
+ /* FIXME: need to let analyzer decide in partial case. */
+ && (!current_alias_ops->ip_partial
+ || !TREE_STATIC (decl)
+ || TREE_PUBLIC (decl)))
+ current_alias_ops->simple_assign (current_alias_ops, var,
+ get_alias_var (pta_global_var));
+ }
+ }
+ /* Functions declared like void f() are *not* equivalent to void
+ f(void). You can pass an argument to them. Thus, we need to
+ create some fake argument that would alias any actuals that get
+ passed to our function. */
+ else
+ {
+ tree fakedecl = create_tmp_var_raw (void_type_node, "fakearg");
+ alias_var fakevar;
+ DECL_CONTEXT (fakedecl) = current_function_decl;
+ fakevar = get_alias_var (fakedecl);
+ VARRAY_PUSH_GENERIC_PTR (params, fakevar);
+ }
+
+ if (!DECL_RESULT (decl))
+ {
+ rdecl = create_tmp_var_raw (TREE_TYPE (TREE_TYPE (decl)), "_rv_");
+ retvar = current_alias_ops->add_var (current_alias_ops, rdecl);
+ DECL_PTA_ALIASVAR (rdecl) = retvar;
+ }
+ else
+ {
+ retvar = current_alias_ops->add_var (current_alias_ops,
+ DECL_RESULT (decl));
+ DECL_PTA_ALIASVAR (DECL_RESULT (decl)) = retvar;
+ }
+ VARRAY_PUSH_GENERIC_PTR (alias_vars, retvar);
+ ALIAS_VAR_VARNUM (retvar) = VARRAY_ACTIVE_SIZE (alias_vars) - 1;
+ avar = current_alias_ops->add_var (current_alias_ops, decl);
+ VARRAY_PUSH_GENERIC_PTR (alias_vars, avar);
+ ALIAS_VAR_VARNUM (avar) = VARRAY_ACTIVE_SIZE (alias_vars) - 1;
+
+ current_alias_ops->function_def (current_alias_ops, avar, params, retvar);
+ DECL_PTA_ALIASVAR (decl) = avar;
+
+ /* FIXME: Also, if this is a defining declaration then add the annotation
+ to all extern definitions of the function. */
+ return avar;
+}
+
+/* Create an alias variable for a pointer-to-member function DECL of
+ type TYPE, it's arguments, and it's return value.
+ Returns the alias_var for the PTF.
+
+ This includes creating alias_var's for
+ - The function itself.
+ - The arguments.
+ - The return value. */
+
+static alias_var
+create_fun_alias_var_ptf (tree decl, tree type)
+{
+ alias_var avar, retvar;
+ tree rdecl;
+ varray_type params = NULL;
+
+ if (DECL_PTA_ALIASVAR (decl))
+ return DECL_PTA_ALIASVAR (decl);
+
+ VARRAY_GENERIC_PTR_INIT (params, 1, "Arguments");
+
+ if (TYPE_ARG_TYPES (type) != NULL)
+ {
+ tree arg;
+ /* FIXME: Handle varargs */
+ for (arg = TYPE_ARG_TYPES (type);
+ arg && TREE_VALUE (arg) != void_type_node;
+ arg = TREE_CHAIN (arg))
+ {
+ tree fakedecl = create_tmp_var_raw (TREE_VALUE (arg), "ptfarg");
+ alias_var var;
+ DECL_CONTEXT (fakedecl) = DECL_CONTEXT (decl);
+ var = get_alias_var (fakedecl);
+ VARRAY_PUSH_GENERIC_PTR (params, var);
+ }
+ }
+ /* Functions declared like void f() are *not* equivalent to void
+ f(void). You can pass an argument to them. Thus, we need to
+ create some fake argument that would alias any actuals that get
+ passed to our function. */
+ else
+ {
+ tree fakedecl = create_tmp_var_raw (void_type_node, "fakearg");
+ alias_var fakevar;
+ DECL_CONTEXT (fakedecl) = DECL_CONTEXT (decl);
+ fakevar = get_alias_var (fakedecl);
+ VARRAY_PUSH_GENERIC_PTR (params, fakevar);
+ }
+
+ rdecl = create_tmp_var_raw (TREE_TYPE (type), "_rv_");
+ retvar = current_alias_ops->add_var (current_alias_ops, rdecl);
+ VARRAY_PUSH_GENERIC_PTR (alias_vars, retvar);
+ ALIAS_VAR_VARNUM (retvar) = VARRAY_ACTIVE_SIZE (alias_vars) - 1;
+
+ avar = current_alias_ops->add_var (current_alias_ops, decl);
+ VARRAY_PUSH_GENERIC_PTR (alias_vars, avar);
+ ALIAS_VAR_VARNUM (avar) = VARRAY_ACTIVE_SIZE (alias_vars) - 1;
+
+ current_alias_ops->function_def (current_alias_ops, avar, params, retvar);
+ DECL_PTA_ALIASVAR (decl) = avar;
+
+ return avar;
+}
+
+/* Create the alias_var for a *_DECL node DECL.
+ Returns the alias_var for DECL.
+
+ This function also handles creation of alias_var's for PTF
+ variables. */
+
+static alias_var
+create_alias_var (tree decl)
+{
+ alias_var avar;
+
+ if (!DECL_P (decl))
+ abort ();
+
+ if (DECL_P (decl))
+ {
+ if (DECL_PTA_ALIASVAR (decl))
+ return DECL_PTA_ALIASVAR (decl);
+ }
+
+ if (POINTER_TYPE_P (TREE_TYPE (decl))
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == FUNCTION_TYPE)
+ {
+ avar = create_fun_alias_var_ptf (decl, TREE_TYPE (TREE_TYPE (decl)));
+ }
+ else
+ avar = current_alias_ops->add_var (current_alias_ops, decl);
+
+ if (DECL_P (decl))
+ {
+ DECL_PTA_ALIASVAR (decl) = avar;
+ }
+
+ VARRAY_PUSH_GENERIC_PTR (alias_vars, avar);
+ ALIAS_VAR_VARNUM (avar) = VARRAY_ACTIVE_SIZE (alias_vars) - 1;
+ return avar;
+}
+
+/* Create points-to sets for the current function. */
+
+static void
+create_alias_vars (void)
+{
+ basic_block bb;
+#if HAVE_BANSHEE
+ if (flag_tree_points_to == PTA_ANDERSEN)
+ current_alias_ops = andersen_alias_ops;
+ else
+#endif
+ {
+ current_alias_ops = NULL;
+ flag_tree_points_to = PTA_NONE;
+ return;
+ }
+
+ pta_global_var = build_decl (VAR_DECL, get_identifier (".pta_global_var"),
+ size_type_node);
+ DECL_ARTIFICIAL (pta_global_var) = 1;
+ TREE_READONLY (pta_global_var) = 1;
+ DECL_EXTERNAL (pta_global_var) = 0;
+ TREE_STATIC (pta_global_var) = 1;
+ TREE_USED (pta_global_var) = 1;
+ DECL_CONTEXT (pta_global_var) = current_function_decl;
+ TREE_THIS_VOLATILE (pta_global_var) = 1;
+ TREE_ADDRESSABLE (pta_global_var) = 0;
+
+ init_alias_vars ();
+
+ DECL_PTA_ALIASVAR (current_function_decl) = NULL;
+ get_alias_var (current_function_decl);
+
+ /* First, walk the variables and their DECL_INITIAL's */
+ if (cfun->unexpanded_var_list)
+ {
+ tree vars, var;
+ for (vars = cfun->unexpanded_var_list; vars; vars = TREE_CHAIN (vars))
+ {
+ var = TREE_VALUE (vars);
+ if (TREE_CODE (var) != LABEL_DECL
+ && decl_function_context (var) == NULL
+ && DECL_INITIAL (var))
+ find_func_aliases (var);
+ }
+ }
+
+ /* Now walk all statements and derive aliases. */
+ FOR_EACH_BB (bb)
+ {
+ block_stmt_iterator bsi;
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ find_func_aliases (bsi_stmt (bsi));
+ }
+
+ pta_global_var = NULL_TREE;
+}
+
+struct tree_opt_pass pass_build_pta =
+{
+ "pta", /* name */
+ NULL, /* gate */
+ create_alias_vars, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_TREE_PTA, /* tv_id */
+ PROP_cfg, /* properties_required */
+ PROP_pta, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0 /* todo_flags_finish */
+};
+
+
+/* Delete created points-to sets. */
+
+static void
+delete_alias_vars (void)
+{
+ size_t i;
+
+ if (flag_tree_points_to != PTA_ANDERSEN)
+ return;
+
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (local_alias_vars); i++)
+ {
+ tree key = VARRAY_TREE (local_alias_vars, i);
+ if (DECL_P (key))
+ DECL_PTA_ALIASVAR (key) = NULL;
+ else
+ abort ();
+ }
+
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (local_alias_varnums); i ++)
+ VARRAY_GENERIC_PTR (alias_vars, VARRAY_INT (local_alias_varnums, i)) = NULL;
+ if (!current_alias_ops->ip && !current_alias_ops->ip_partial)
+ {
+ /* VARRAY_CLEAR (alias_vars); */
+ VARRAY_CLEAR (local_alias_vars);
+ VARRAY_CLEAR (local_alias_varnums);
+ }
+ BITMAP_XFREE (addrargs);
+ current_alias_ops->cleanup (current_alias_ops);
+}
+
+struct tree_opt_pass pass_del_pta =
+{
+ "pta", /* name */
+ NULL, /* gate */
+ delete_alias_vars, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_TREE_PTA, /* tv_id */
+ PROP_pta, /* properties_required */
+ 0, /* properties_provided */
+ PROP_pta, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0 /* todo_flags_finish */
+};
+
+
+/* Initialize points-to analysis machinery. */
+
+void
+init_alias_vars (void)
+{
+ current_alias_ops->init (current_alias_ops);
+ addrargs = BITMAP_XMALLOC ();
+ VARRAY_TREE_INIT (local_alias_vars, 10, "Local alias vars");
+ VARRAY_INT_INIT (local_alias_varnums, 10, "Local alias varnums");
+ if ((!current_alias_ops->ip && !current_alias_ops->ip_partial)
+ || alias_vars == NULL)
+ VARRAY_GENERIC_PTR_INIT (alias_vars, 10, "Alias vars");
+}
+
+/* Return true if PTR can't point to anything (i.e. it has an empty
+ points-to set. */
+bool
+empty_points_to_set (tree ptr)
+{
+ alias_var ptrtv;
+
+#if !FIELD_BASED
+#else
+ if (TREE_CODE (ptr) == COMPONENT_REF)
+ ptr = TREE_OPERAND (ptr, 1);
+#endif
+
+ if (DECL_P (ptr))
+ {
+ ptrtv = DECL_PTA_ALIASVAR (ptr);
+ if (!ptrtv)
+ return true;
+ }
+ else
+ abort ();
+
+ return current_alias_ops->empty_points_to_set (current_alias_ops, ptrtv);
+}
+
+/* Return true if PTR and VAR have the same points-to set. */
+
+bool
+same_points_to_set (tree ptr, tree var)
+{
+ alias_var ptrtv, vartv;
+
+#if !FIELD_BASED
+#else
+ if (TREE_CODE (ptr) == COMPONENT_REF)
+ ptr = TREE_OPERAND (ptr, 1);
+ if (TREE_CODE (var) == COMPONENT_REF)
+ var = TREE_OPERAND (var, 1);
+#endif
+
+ if (ptr == var)
+ return true;
+
+ if (DECL_P (ptr))
+ {
+ ptrtv = DECL_PTA_ALIASVAR (ptr);
+ if (!ptrtv)
+ return false;
+ }
+ else
+ abort ();
+
+ if (DECL_P (var))
+ {
+ vartv = DECL_PTA_ALIASVAR (var);
+ if (!vartv)
+ return false;
+ }
+ else
+ abort ();
+
+ return current_alias_ops->same_points_to_set (current_alias_ops, vartv, ptrtv);
+}
+
+/* Determine whether two variables (PTR and VAR) may-alias.
+ Returns TRUE if PTR may-alias VAR. */
+
+bool
+ptr_may_alias_var (tree ptr, tree var)
+{
+ alias_var ptrtv, vartv;
+
+#if !FIELD_BASED
+#else
+ if (TREE_CODE (ptr) == COMPONENT_REF)
+ ptr = TREE_OPERAND (ptr, 1);
+ if (TREE_CODE (var) == COMPONENT_REF)
+ var = TREE_OPERAND (var, 1);
+#endif
+
+ if (ptr == var)
+ return true;
+
+ if (DECL_P (ptr))
+ {
+ ptrtv = DECL_PTA_ALIASVAR (ptr);
+ if (!ptrtv)
+ return false;
+ }
+ else
+ abort ();
+
+ if (DECL_P (var))
+ {
+ vartv = DECL_PTA_ALIASVAR (var);
+ if (!vartv)
+ return false;
+ }
+ else
+ abort ();
+
+ return current_alias_ops->may_alias (current_alias_ops, ptrtv, vartv);
+}
+
+#define MASK_POINTER(P) ((unsigned)((unsigned long)(P) & 0xffff))
+
+const char *
+alias_get_name (tree t)
+{
+ const char *name;
+
+#if FIELD_BASED
+ if (TREE_CODE (t) == FIELD_DECL)
+ {
+ /* First get the name of the field, then the prefix, then smash them
+ together. */
+ const char *fieldname = IDENTIFIER_POINTER (DECL_NAME (t));
+ const char *prefix = alias_get_name (DECL_CONTEXT (t));
+ char *smashed;
+ size_t neededlen = strlen (fieldname) + strlen (prefix) + 2;
+ smashed = ggc_alloc (neededlen);
+ sprintf (smashed, "%s.%s", prefix, fieldname);
+ name = smashed;
+
+ }
+ else if (TYPE_P (t))
+ {
+ if (TYPE_NAME (t) && IDENTIFIER_POINTER (TYPE_NAME (t)))
+ name = IDENTIFIER_POINTER (TYPE_NAME (t));
+ else
+ name = "<unnamed type>";
+ }
+ else
+#endif
+ {
+ if (TREE_CODE (t) == FUNCTION_DECL)
+ name = IDENTIFIER_POINTER (DECL_NAME (t));
+ else if (TREE_CODE (t) == RESULT_DECL)
+ name = "<return value>";
+ else
+ name = get_name (t);
+ }
+
+ if (!name)
+ {
+ char *namep;
+ /* 2 = UF
+ 4 = the masked pointer
+ 2 = the <> around it
+ 1 = the terminator. */
+ namep = ggc_alloc (2 + 4 + 2 + 1);
+ sprintf (namep, "<UV%x>", MASK_POINTER (t));
+ return namep;
+ }
+
+ return name;
+}
+
+#include "gt-tree-alias-common.h"