gcc/gcc/tree-alias-common.c

1252 lines
34 KiB
C

/* 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-gimple.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;
gcc_assert (TREE_CODE (decl) != FIELD_DECL);
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 (TREE_STATIC (decl) && 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:
case ARRAY_RANGE_REF:
{
/* Find the first non-array ref, and return its alias variable. */
tree p;
for (p = expr;
TREE_CODE (p) == ARRAY_REF || TREE_CODE (p) == ARRAY_RANGE_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:
gcc_unreachable ();
}
}
/* 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);
if (TREE_CODE (op1) == WITH_SIZE_EXPR)
op1 = TREE_OPERAND (op1, 0);
}
/* 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;
gcc_assert (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
&& TREE_STATIC (var)
&& 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;
}
static bool
gate_pta (void)
{
#ifdef HAVE_BANSHEE
return flag_tree_points_to != PTA_NONE;
#else
return false;
#endif
}
struct tree_opt_pass pass_build_pta =
{
"pta", /* name */
gate_pta, /* 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 */
0 /* letter */
};
/* Delete created points-to sets. */
static void
delete_alias_vars (void)
{
size_t i;
for (i = 0; i < VARRAY_ACTIVE_SIZE (local_alias_vars); i++)
{
tree key = VARRAY_TREE (local_alias_vars, i);
gcc_assert (DECL_P (key));
DECL_PTA_ALIASVAR (key) = NULL;
}
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 */
gate_pta, /* 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 */
0 /* letter */
};
/* 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
gcc_assert (DECL_P (ptr));
ptrtv = DECL_PTA_ALIASVAR (ptr);
if (!ptrtv)
return true;
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;
gcc_assert (DECL_P (ptr));
ptrtv = DECL_PTA_ALIASVAR (ptr);
if (!ptrtv)
return false;
gcc_assert (DECL_P (var));
vartv = DECL_PTA_ALIASVAR (var);
if (!vartv)
return false;
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;
gcc_assert (DECL_P (ptr));
ptrtv = DECL_PTA_ALIASVAR (ptr);
if (!ptrtv)
return false;
gcc_assert (DECL_P (var));
vartv = DECL_PTA_ALIASVAR (var);
if (!vartv)
return false;
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"