2522 lines
76 KiB
C
2522 lines
76 KiB
C
/* Alias analysis for trees.
|
|
Copyright (C) 2004 Free Software Foundation, Inc.
|
|
Contributed by Diego Novillo <dnovillo@redhat.com>
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, 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; see the file COPYING. If not, write to
|
|
the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
Boston, MA 02111-1307, USA. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "tm.h"
|
|
#include "tree.h"
|
|
#include "rtl.h"
|
|
#include "tm_p.h"
|
|
#include "hard-reg-set.h"
|
|
#include "basic-block.h"
|
|
#include "timevar.h"
|
|
#include "expr.h"
|
|
#include "ggc.h"
|
|
#include "langhooks.h"
|
|
#include "flags.h"
|
|
#include "function.h"
|
|
#include "diagnostic.h"
|
|
#include "tree-dump.h"
|
|
#include "tree-gimple.h"
|
|
#include "tree-flow.h"
|
|
#include "tree-inline.h"
|
|
#include "tree-pass.h"
|
|
#include "convert.h"
|
|
#include "params.h"
|
|
|
|
|
|
/* Structure to map a variable to its alias set and keep track of the
|
|
virtual operands that will be needed to represent it. */
|
|
struct alias_map_d
|
|
{
|
|
/* Variable and its alias set. */
|
|
tree var;
|
|
HOST_WIDE_INT set;
|
|
|
|
/* Total number of virtual operands that will be needed to represent
|
|
all the aliases of VAR. */
|
|
long total_alias_vops;
|
|
|
|
/* Nonzero if the aliases for this memory tag have been grouped
|
|
already. Used in group_aliases. */
|
|
unsigned int grouped_p : 1;
|
|
|
|
/* Set of variables aliased with VAR. This is the exact same
|
|
information contained in VAR_ANN (VAR)->MAY_ALIASES, but in
|
|
bitmap form to speed up alias grouping. */
|
|
sbitmap may_aliases;
|
|
};
|
|
|
|
|
|
/* Alias information used by compute_may_aliases and its helpers. */
|
|
struct alias_info
|
|
{
|
|
/* SSA names visited while collecting points-to information. If bit I
|
|
is set, it means that SSA variable with version I has already been
|
|
visited. */
|
|
sbitmap ssa_names_visited;
|
|
|
|
/* Array of SSA_NAME pointers processed by the points-to collector. */
|
|
varray_type processed_ptrs;
|
|
|
|
/* Variables whose address is still needed. */
|
|
bitmap addresses_needed;
|
|
|
|
/* ADDRESSABLE_VARS contains all the global variables and locals that
|
|
have had their address taken. */
|
|
struct alias_map_d **addressable_vars;
|
|
size_t num_addressable_vars;
|
|
|
|
/* POINTERS contains all the _DECL pointers with unique memory tags
|
|
that have been referenced in the program. */
|
|
struct alias_map_d **pointers;
|
|
size_t num_pointers;
|
|
|
|
/* Number of function calls found in the program. */
|
|
size_t num_calls_found;
|
|
|
|
/* Array of counters to keep track of how many times each pointer has
|
|
been dereferenced in the program. This is used by the alias grouping
|
|
heuristic in compute_flow_insensitive_aliasing. */
|
|
varray_type num_references;
|
|
|
|
/* Total number of virtual operands that will be needed to represent
|
|
all the aliases of all the pointers found in the program. */
|
|
long total_alias_vops;
|
|
|
|
/* Variables that have been written to. */
|
|
bitmap written_vars;
|
|
|
|
/* Pointers that have been used in an indirect store operation. */
|
|
bitmap dereferenced_ptrs_store;
|
|
|
|
/* Pointers that have been used in an indirect load operation. */
|
|
bitmap dereferenced_ptrs_load;
|
|
};
|
|
|
|
|
|
/* Counters used to display statistics on alias analysis. */
|
|
struct alias_stats_d
|
|
{
|
|
unsigned int alias_queries;
|
|
unsigned int alias_mayalias;
|
|
unsigned int alias_noalias;
|
|
unsigned int simple_queries;
|
|
unsigned int simple_resolved;
|
|
unsigned int tbaa_queries;
|
|
unsigned int tbaa_resolved;
|
|
};
|
|
|
|
|
|
/* Local variables. */
|
|
static struct alias_stats_d alias_stats;
|
|
|
|
/* Local functions. */
|
|
static void compute_flow_insensitive_aliasing (struct alias_info *);
|
|
static void dump_alias_stats (FILE *);
|
|
static bool may_alias_p (tree, HOST_WIDE_INT, tree, HOST_WIDE_INT);
|
|
static tree create_memory_tag (tree type, bool is_type_tag);
|
|
static tree get_tmt_for (tree, struct alias_info *);
|
|
static tree get_nmt_for (tree);
|
|
static void add_may_alias (tree, tree);
|
|
static void replace_may_alias (tree, size_t, tree);
|
|
static struct alias_info *init_alias_info (void);
|
|
static void delete_alias_info (struct alias_info *);
|
|
static void compute_points_to_and_addr_escape (struct alias_info *);
|
|
static void compute_flow_sensitive_aliasing (struct alias_info *);
|
|
static void setup_pointers_and_addressables (struct alias_info *);
|
|
static bool collect_points_to_info_r (tree, tree, void *);
|
|
static bool is_escape_site (tree, size_t *);
|
|
static void add_pointed_to_var (struct alias_info *, tree, tree);
|
|
static void create_global_var (void);
|
|
static void collect_points_to_info_for (struct alias_info *, tree);
|
|
static bool ptr_is_dereferenced_by (tree, tree, bool *);
|
|
static void maybe_create_global_var (struct alias_info *ai);
|
|
static void group_aliases (struct alias_info *);
|
|
static void set_pt_anything (tree ptr);
|
|
static void set_pt_malloc (tree ptr);
|
|
|
|
/* Global declarations. */
|
|
|
|
/* Call clobbered variables in the function. If bit I is set, then
|
|
REFERENCED_VARS (I) is call-clobbered. */
|
|
bitmap call_clobbered_vars;
|
|
|
|
/* Addressable variables in the function. If bit I is set, then
|
|
REFERENCED_VARS (I) has had its address taken. Note that
|
|
CALL_CLOBBERED_VARS and ADDRESSABLE_VARS are not related. An
|
|
addressable variable is not necessarily call-clobbered (e.g., a
|
|
local addressable whose address does not escape) and not all
|
|
call-clobbered variables are addressable (e.g., a local static
|
|
variable). */
|
|
bitmap addressable_vars;
|
|
|
|
/* When the program has too many call-clobbered variables and call-sites,
|
|
this variable is used to represent the clobbering effects of function
|
|
calls. In these cases, all the call clobbered variables in the program
|
|
are forced to alias this variable. This reduces compile times by not
|
|
having to keep track of too many V_MAY_DEF expressions at call sites. */
|
|
tree global_var;
|
|
|
|
|
|
/* Compute may-alias information for every variable referenced in function
|
|
FNDECL.
|
|
|
|
Alias analysis proceeds in 3 main phases:
|
|
|
|
1- Points-to and escape analysis.
|
|
|
|
This phase walks the use-def chains in the SSA web looking for three
|
|
things:
|
|
|
|
* Assignments of the form P_i = &VAR
|
|
* Assignments of the form P_i = malloc()
|
|
* Pointers and ADDR_EXPR that escape the current function.
|
|
|
|
The concept of 'escaping' is the same one used in the Java world. When
|
|
a pointer or an ADDR_EXPR escapes, it means that it has been exposed
|
|
outside of the current function. So, assignment to global variables,
|
|
function arguments and returning a pointer are all escape sites, as are
|
|
conversions between pointers and integers.
|
|
|
|
This is where we are currently limited. Since not everything is renamed
|
|
into SSA, we lose track of escape properties when a pointer is stashed
|
|
inside a field in a structure, for instance. In those cases, we are
|
|
assuming that the pointer does escape.
|
|
|
|
We use escape analysis to determine whether a variable is
|
|
call-clobbered. Simply put, if an ADDR_EXPR escapes, then the variable
|
|
is call-clobbered. If a pointer P_i escapes, then all the variables
|
|
pointed-to by P_i (and its memory tag) also escape.
|
|
|
|
2- Compute flow-sensitive aliases
|
|
|
|
We have two classes of memory tags. Memory tags associated with the
|
|
pointed-to data type of the pointers in the program. These tags are
|
|
called "type memory tag" (TMT). The other class are those associated
|
|
with SSA_NAMEs, called "name memory tag" (NMT). The basic idea is that
|
|
when adding operands for an INDIRECT_REF *P_i, we will first check
|
|
whether P_i has a name tag, if it does we use it, because that will have
|
|
more precise aliasing information. Otherwise, we use the standard type
|
|
tag.
|
|
|
|
In this phase, we go through all the pointers we found in points-to
|
|
analysis and create alias sets for the name memory tags associated with
|
|
each pointer P_i. If P_i escapes, we mark call-clobbered the variables
|
|
it points to and its tag.
|
|
|
|
|
|
3- Compute flow-insensitive aliases
|
|
|
|
This pass will compare the alias set of every type memory tag and every
|
|
addressable variable found in the program. Given a type memory tag TMT
|
|
and an addressable variable V. If the alias sets of TMT and V conflict
|
|
(as computed by may_alias_p), then V is marked as an alias tag and added
|
|
to the alias set of TMT.
|
|
|
|
For instance, consider the following function:
|
|
|
|
foo (int i)
|
|
{
|
|
int *p, a, b;
|
|
|
|
if (i > 10)
|
|
p = &a;
|
|
else
|
|
p = &b;
|
|
|
|
*p = 3;
|
|
a = b + 2;
|
|
return *p;
|
|
}
|
|
|
|
After aliasing analysis has finished, the type memory tag for pointer
|
|
'p' will have two aliases, namely variables 'a' and 'b'. Every time
|
|
pointer 'p' is dereferenced, we want to mark the operation as a
|
|
potential reference to 'a' and 'b'.
|
|
|
|
foo (int i)
|
|
{
|
|
int *p, a, b;
|
|
|
|
if (i_2 > 10)
|
|
p_4 = &a;
|
|
else
|
|
p_6 = &b;
|
|
# p_1 = PHI <p_4(1), p_6(2)>;
|
|
|
|
# a_7 = V_MAY_DEF <a_3>;
|
|
# b_8 = V_MAY_DEF <b_5>;
|
|
*p_1 = 3;
|
|
|
|
# a_9 = V_MAY_DEF <a_7>
|
|
# VUSE <b_8>
|
|
a_9 = b_8 + 2;
|
|
|
|
# VUSE <a_9>;
|
|
# VUSE <b_8>;
|
|
return *p_1;
|
|
}
|
|
|
|
In certain cases, the list of may aliases for a pointer may grow too
|
|
large. This may cause an explosion in the number of virtual operands
|
|
inserted in the code. Resulting in increased memory consumption and
|
|
compilation time.
|
|
|
|
When the number of virtual operands needed to represent aliased
|
|
loads and stores grows too large (configurable with @option{--param
|
|
max-aliased-vops}), alias sets are grouped to avoid severe
|
|
compile-time slow downs and memory consumption. See group_aliases. */
|
|
|
|
static void
|
|
compute_may_aliases (void)
|
|
{
|
|
struct alias_info *ai;
|
|
|
|
memset (&alias_stats, 0, sizeof (alias_stats));
|
|
|
|
/* Initialize aliasing information. */
|
|
ai = init_alias_info ();
|
|
|
|
/* For each pointer P_i, determine the sets of variables that P_i may
|
|
point-to. For every addressable variable V, determine whether the
|
|
address of V escapes the current function, making V call-clobbered
|
|
(i.e., whether &V is stored in a global variable or if its passed as a
|
|
function call argument). */
|
|
compute_points_to_and_addr_escape (ai);
|
|
|
|
/* Collect all pointers and addressable variables, compute alias sets,
|
|
create memory tags for pointers and promote variables whose address is
|
|
not needed anymore. */
|
|
setup_pointers_and_addressables (ai);
|
|
|
|
/* Compute flow-sensitive, points-to based aliasing for all the name
|
|
memory tags. Note that this pass needs to be done before flow
|
|
insensitive analysis because it uses the points-to information
|
|
gathered before to mark call-clobbered type tags. */
|
|
compute_flow_sensitive_aliasing (ai);
|
|
|
|
/* Compute type-based flow-insensitive aliasing for all the type
|
|
memory tags. */
|
|
compute_flow_insensitive_aliasing (ai);
|
|
|
|
/* If the program has too many call-clobbered variables and/or function
|
|
calls, create .GLOBAL_VAR and use it to model call-clobbering
|
|
semantics at call sites. This reduces the number of virtual operands
|
|
considerably, improving compile times at the expense of lost
|
|
aliasing precision. */
|
|
maybe_create_global_var (ai);
|
|
|
|
/* Debugging dumps. */
|
|
if (dump_file)
|
|
{
|
|
dump_referenced_vars (dump_file);
|
|
if (dump_flags & TDF_STATS)
|
|
dump_alias_stats (dump_file);
|
|
dump_points_to_info (dump_file);
|
|
dump_alias_info (dump_file);
|
|
}
|
|
|
|
/* Deallocate memory used by aliasing data structures. */
|
|
delete_alias_info (ai);
|
|
}
|
|
|
|
struct tree_opt_pass pass_may_alias =
|
|
{
|
|
"alias", /* name */
|
|
NULL, /* gate */
|
|
compute_may_aliases, /* execute */
|
|
NULL, /* sub */
|
|
NULL, /* next */
|
|
0, /* static_pass_number */
|
|
TV_TREE_MAY_ALIAS, /* tv_id */
|
|
PROP_cfg | PROP_ssa, /* properties_required */
|
|
PROP_alias, /* properties_provided */
|
|
0, /* properties_destroyed */
|
|
0, /* todo_flags_start */
|
|
TODO_dump_func | TODO_rename_vars
|
|
| TODO_ggc_collect | TODO_verify_ssa, /* todo_flags_finish */
|
|
0 /* letter */
|
|
};
|
|
|
|
/* Count the number of calls in the function and conditionally
|
|
create GLOBAL_VAR. This is performed before translation
|
|
into SSA (and thus before alias analysis) to avoid compile time
|
|
and memory utilization explosions in functions with many
|
|
of calls and call clobbered variables. */
|
|
|
|
static void
|
|
count_calls_and_maybe_create_global_var (void)
|
|
{
|
|
struct alias_info ai;
|
|
basic_block bb;
|
|
bool temp;
|
|
|
|
memset (&ai, 0, sizeof (struct alias_info));
|
|
|
|
/* First count the number of calls in the IL. */
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
block_stmt_iterator si;
|
|
|
|
for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
|
|
{
|
|
tree stmt = bsi_stmt (si);
|
|
|
|
if (get_call_expr_in (stmt) != NULL_TREE)
|
|
ai.num_calls_found++;
|
|
}
|
|
}
|
|
|
|
/* If there are no call clobbered variables, then maybe_create_global_var
|
|
will always create a GLOBAL_VAR. At this point we do not want that
|
|
behavior. So we turn on one bit in CALL_CLOBBERED_VARs, call
|
|
maybe_create_global_var, then reset the bit to its original state. */
|
|
temp = bitmap_bit_p (call_clobbered_vars, 0);
|
|
bitmap_set_bit (call_clobbered_vars, 0);
|
|
maybe_create_global_var (&ai);
|
|
if (!temp)
|
|
bitmap_clear_bit (call_clobbered_vars, 0);
|
|
}
|
|
|
|
struct tree_opt_pass pass_maybe_create_global_var =
|
|
{
|
|
"maybe_create_global_var", /* name */
|
|
NULL, /* gate */
|
|
count_calls_and_maybe_create_global_var, /* execute */
|
|
NULL, /* sub */
|
|
NULL, /* next */
|
|
0, /* static_pass_number */
|
|
TV_TREE_MAY_ALIAS, /* tv_id */
|
|
PROP_cfg, /* properties_required */
|
|
0, /* properties_provided */
|
|
0, /* properties_destroyed */
|
|
0, /* todo_flags_start */
|
|
0, /* todo_flags_finish */
|
|
0 /* letter */
|
|
};
|
|
|
|
/* Initialize the data structures used for alias analysis. */
|
|
|
|
static struct alias_info *
|
|
init_alias_info (void)
|
|
{
|
|
struct alias_info *ai;
|
|
static bool aliases_computed_p = false;
|
|
|
|
ai = xcalloc (1, sizeof (struct alias_info));
|
|
ai->ssa_names_visited = sbitmap_alloc (num_ssa_names);
|
|
sbitmap_zero (ai->ssa_names_visited);
|
|
VARRAY_TREE_INIT (ai->processed_ptrs, 50, "processed_ptrs");
|
|
ai->addresses_needed = BITMAP_XMALLOC ();
|
|
VARRAY_UINT_INIT (ai->num_references, num_referenced_vars, "num_references");
|
|
ai->written_vars = BITMAP_XMALLOC ();
|
|
ai->dereferenced_ptrs_store = BITMAP_XMALLOC ();
|
|
ai->dereferenced_ptrs_load = BITMAP_XMALLOC ();
|
|
|
|
/* If aliases have been computed before, clear existing information. */
|
|
if (aliases_computed_p)
|
|
{
|
|
unsigned i;
|
|
basic_block bb;
|
|
|
|
/* Make sure that every statement has a valid set of operands.
|
|
If a statement needs to be scanned for operands while we
|
|
compute aliases, it may get erroneous operands because all
|
|
the alias relations are not built at that point.
|
|
FIXME: This code will become obsolete when operands are not
|
|
lazily updated. */
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
block_stmt_iterator si;
|
|
for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
|
|
get_stmt_operands (bsi_stmt (si));
|
|
}
|
|
|
|
/* Similarly, clear the set of addressable variables. In this
|
|
case, we can just clear the set because addressability is
|
|
only computed here. */
|
|
bitmap_clear (addressable_vars);
|
|
|
|
/* Clear flow-insensitive alias information from each symbol. */
|
|
for (i = 0; i < num_referenced_vars; i++)
|
|
{
|
|
tree var = referenced_var (i);
|
|
var_ann_t ann = var_ann (var);
|
|
|
|
ann->is_alias_tag = 0;
|
|
ann->may_aliases = NULL;
|
|
|
|
/* Since we are about to re-discover call-clobbered
|
|
variables, clear the call-clobbered flag. Variables that
|
|
are intrinsically call-clobbered (globals, local statics,
|
|
etc) will not be marked by the aliasing code, so we can't
|
|
remove them from CALL_CLOBBERED_VARS. */
|
|
if (ann->mem_tag_kind != NOT_A_TAG || !is_global_var (var))
|
|
clear_call_clobbered (var);
|
|
}
|
|
|
|
/* Clear flow-sensitive points-to information from each SSA name. */
|
|
for (i = 1; i < num_ssa_names; i++)
|
|
{
|
|
tree name = ssa_name (i);
|
|
|
|
if (!name || !POINTER_TYPE_P (TREE_TYPE (name)))
|
|
continue;
|
|
|
|
if (SSA_NAME_PTR_INFO (name))
|
|
{
|
|
struct ptr_info_def *pi = SSA_NAME_PTR_INFO (name);
|
|
|
|
/* Clear all the flags but keep the name tag to
|
|
avoid creating new temporaries unnecessarily. If
|
|
this pointer is found to point to a subset or
|
|
superset of its former points-to set, then a new
|
|
tag will need to be created in create_name_tags. */
|
|
pi->pt_anything = 0;
|
|
pi->pt_malloc = 0;
|
|
pi->value_escapes_p = 0;
|
|
pi->is_dereferenced = 0;
|
|
if (pi->pt_vars)
|
|
bitmap_clear (pi->pt_vars);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Next time, we will need to reset alias information. */
|
|
aliases_computed_p = true;
|
|
|
|
return ai;
|
|
}
|
|
|
|
|
|
/* Deallocate memory used by alias analysis. */
|
|
|
|
static void
|
|
delete_alias_info (struct alias_info *ai)
|
|
{
|
|
size_t i;
|
|
|
|
sbitmap_free (ai->ssa_names_visited);
|
|
ai->processed_ptrs = NULL;
|
|
BITMAP_XFREE (ai->addresses_needed);
|
|
|
|
for (i = 0; i < ai->num_addressable_vars; i++)
|
|
{
|
|
sbitmap_free (ai->addressable_vars[i]->may_aliases);
|
|
free (ai->addressable_vars[i]);
|
|
}
|
|
free (ai->addressable_vars);
|
|
|
|
for (i = 0; i < ai->num_pointers; i++)
|
|
{
|
|
sbitmap_free (ai->pointers[i]->may_aliases);
|
|
free (ai->pointers[i]);
|
|
}
|
|
free (ai->pointers);
|
|
|
|
ai->num_references = NULL;
|
|
BITMAP_XFREE (ai->written_vars);
|
|
BITMAP_XFREE (ai->dereferenced_ptrs_store);
|
|
BITMAP_XFREE (ai->dereferenced_ptrs_load);
|
|
|
|
free (ai);
|
|
}
|
|
|
|
|
|
/* Walk use-def chains for pointer PTR to determine what variables is PTR
|
|
pointing to. */
|
|
|
|
static void
|
|
collect_points_to_info_for (struct alias_info *ai, tree ptr)
|
|
{
|
|
gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr)));
|
|
|
|
if (!TEST_BIT (ai->ssa_names_visited, SSA_NAME_VERSION (ptr)))
|
|
{
|
|
SET_BIT (ai->ssa_names_visited, SSA_NAME_VERSION (ptr));
|
|
walk_use_def_chains (ptr, collect_points_to_info_r, ai, true);
|
|
VARRAY_PUSH_TREE (ai->processed_ptrs, ptr);
|
|
}
|
|
}
|
|
|
|
|
|
/* Helper for ptr_is_dereferenced_by. Called by walk_tree to look for
|
|
(ALIGN/MISALIGNED_)INDIRECT_REF nodes for the pointer passed in DATA. */
|
|
|
|
static tree
|
|
find_ptr_dereference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
|
|
{
|
|
tree ptr = (tree) data;
|
|
|
|
if (INDIRECT_REF_P (*tp)
|
|
&& TREE_OPERAND (*tp, 0) == ptr)
|
|
return *tp;
|
|
|
|
return NULL_TREE;
|
|
}
|
|
|
|
|
|
/* Return true if STMT contains (ALIGN/MISALIGNED_)INDIRECT_REF <PTR>.
|
|
*IS_STORE is set to 'true' if the dereference is on the LHS of an
|
|
assignment. */
|
|
|
|
static bool
|
|
ptr_is_dereferenced_by (tree ptr, tree stmt, bool *is_store)
|
|
{
|
|
*is_store = false;
|
|
|
|
if (TREE_CODE (stmt) == MODIFY_EXPR
|
|
|| (TREE_CODE (stmt) == RETURN_EXPR
|
|
&& TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR))
|
|
{
|
|
tree e, lhs, rhs;
|
|
|
|
e = (TREE_CODE (stmt) == RETURN_EXPR) ? TREE_OPERAND (stmt, 0) : stmt;
|
|
lhs = TREE_OPERAND (e, 0);
|
|
rhs = TREE_OPERAND (e, 1);
|
|
|
|
if (EXPR_P (lhs)
|
|
&& walk_tree (&lhs, find_ptr_dereference, ptr, NULL))
|
|
{
|
|
*is_store = true;
|
|
return true;
|
|
}
|
|
else if (EXPR_P (rhs)
|
|
&& walk_tree (&rhs, find_ptr_dereference, ptr, NULL))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if (TREE_CODE (stmt) == ASM_EXPR)
|
|
{
|
|
if (walk_tree (&ASM_OUTPUTS (stmt), find_ptr_dereference, ptr, NULL)
|
|
|| walk_tree (&ASM_CLOBBERS (stmt), find_ptr_dereference, ptr, NULL))
|
|
{
|
|
*is_store = true;
|
|
return true;
|
|
}
|
|
else if (walk_tree (&ASM_INPUTS (stmt), find_ptr_dereference, ptr, NULL))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Traverse use-def links for all the pointers in the program to collect
|
|
address escape and points-to information.
|
|
|
|
This is loosely based on the same idea described in R. Hasti and S.
|
|
Horwitz, ``Using static single assignment form to improve
|
|
flow-insensitive pointer analysis,'' in SIGPLAN Conference on
|
|
Programming Language Design and Implementation, pp. 97-105, 1998. */
|
|
|
|
static void
|
|
compute_points_to_and_addr_escape (struct alias_info *ai)
|
|
{
|
|
basic_block bb;
|
|
unsigned i;
|
|
tree op;
|
|
ssa_op_iter iter;
|
|
|
|
timevar_push (TV_TREE_PTA);
|
|
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
bb_ann_t block_ann = bb_ann (bb);
|
|
block_stmt_iterator si;
|
|
|
|
for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
|
|
{
|
|
bitmap addr_taken;
|
|
tree stmt = bsi_stmt (si);
|
|
bool stmt_escapes_p = is_escape_site (stmt, &ai->num_calls_found);
|
|
bitmap_iterator bi;
|
|
|
|
/* Mark all the variables whose address are taken by the
|
|
statement. Note that this will miss all the addresses taken
|
|
in PHI nodes (those are discovered while following the use-def
|
|
chains). */
|
|
get_stmt_operands (stmt);
|
|
addr_taken = addresses_taken (stmt);
|
|
if (addr_taken)
|
|
EXECUTE_IF_SET_IN_BITMAP (addr_taken, 0, i, bi)
|
|
{
|
|
tree var = referenced_var (i);
|
|
bitmap_set_bit (ai->addresses_needed, var_ann (var)->uid);
|
|
if (stmt_escapes_p)
|
|
mark_call_clobbered (var);
|
|
}
|
|
|
|
if (stmt_escapes_p)
|
|
block_ann->has_escape_site = 1;
|
|
|
|
FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
|
|
{
|
|
var_ann_t v_ann = var_ann (SSA_NAME_VAR (op));
|
|
struct ptr_info_def *pi;
|
|
bool is_store;
|
|
|
|
/* If the operand's variable may be aliased, keep track
|
|
of how many times we've referenced it. This is used
|
|
for alias grouping in compute_flow_sensitive_aliasing.
|
|
Note that we don't need to grow AI->NUM_REFERENCES
|
|
because we are processing regular variables, not
|
|
memory tags (the array's initial size is set to
|
|
NUM_REFERENCED_VARS). */
|
|
if (may_be_aliased (SSA_NAME_VAR (op)))
|
|
(VARRAY_UINT (ai->num_references, v_ann->uid))++;
|
|
|
|
if (!POINTER_TYPE_P (TREE_TYPE (op)))
|
|
continue;
|
|
|
|
collect_points_to_info_for (ai, op);
|
|
|
|
pi = SSA_NAME_PTR_INFO (op);
|
|
if (ptr_is_dereferenced_by (op, stmt, &is_store))
|
|
{
|
|
/* Mark OP as dereferenced. In a subsequent pass,
|
|
dereferenced pointers that point to a set of
|
|
variables will be assigned a name tag to alias
|
|
all the variables OP points to. */
|
|
pi->is_dereferenced = 1;
|
|
|
|
/* Keep track of how many time we've dereferenced each
|
|
pointer. Again, we don't need to grow
|
|
AI->NUM_REFERENCES because we're processing
|
|
existing program variables. */
|
|
(VARRAY_UINT (ai->num_references, v_ann->uid))++;
|
|
|
|
/* If this is a store operation, mark OP as being
|
|
dereferenced to store, otherwise mark it as being
|
|
dereferenced to load. */
|
|
if (is_store)
|
|
bitmap_set_bit (ai->dereferenced_ptrs_store, v_ann->uid);
|
|
else
|
|
bitmap_set_bit (ai->dereferenced_ptrs_load, v_ann->uid);
|
|
}
|
|
else if (stmt_escapes_p)
|
|
{
|
|
/* Note that even if STMT is an escape point, pointer OP
|
|
will not escape if it is being dereferenced. That's
|
|
why we only check for escape points if OP is not
|
|
dereferenced by STMT. */
|
|
pi->value_escapes_p = 1;
|
|
|
|
/* If the statement makes a function call, assume
|
|
that pointer OP will be dereferenced in a store
|
|
operation inside the called function. */
|
|
if (get_call_expr_in (stmt))
|
|
{
|
|
bitmap_set_bit (ai->dereferenced_ptrs_store, v_ann->uid);
|
|
pi->is_dereferenced = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Update reference counter for definitions to any
|
|
potentially aliased variable. This is used in the alias
|
|
grouping heuristics. */
|
|
FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_DEF)
|
|
{
|
|
tree var = SSA_NAME_VAR (op);
|
|
var_ann_t ann = var_ann (var);
|
|
bitmap_set_bit (ai->written_vars, ann->uid);
|
|
if (may_be_aliased (var))
|
|
(VARRAY_UINT (ai->num_references, ann->uid))++;
|
|
|
|
if (POINTER_TYPE_P (TREE_TYPE (op)))
|
|
collect_points_to_info_for (ai, op);
|
|
}
|
|
|
|
/* Mark variables in V_MAY_DEF operands as being written to. */
|
|
FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_VIRTUAL_DEFS)
|
|
{
|
|
tree var = SSA_NAME_VAR (op);
|
|
var_ann_t ann = var_ann (var);
|
|
bitmap_set_bit (ai->written_vars, ann->uid);
|
|
}
|
|
|
|
/* After promoting variables and computing aliasing we will
|
|
need to re-scan most statements. FIXME: Try to minimize the
|
|
number of statements re-scanned. It's not really necessary to
|
|
re-scan *all* statements. */
|
|
modify_stmt (stmt);
|
|
}
|
|
}
|
|
|
|
timevar_pop (TV_TREE_PTA);
|
|
}
|
|
|
|
|
|
/* Create name tags for all the pointers that have been dereferenced.
|
|
We only create a name tag for a pointer P if P is found to point to
|
|
a set of variables (so that we can alias them to *P) or if it is
|
|
the result of a call to malloc (which means that P cannot point to
|
|
anything else nor alias any other variable).
|
|
|
|
If two pointers P and Q point to the same set of variables, they
|
|
are assigned the same name tag. */
|
|
|
|
static void
|
|
create_name_tags (struct alias_info *ai)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < VARRAY_ACTIVE_SIZE (ai->processed_ptrs); i++)
|
|
{
|
|
tree ptr = VARRAY_TREE (ai->processed_ptrs, i);
|
|
struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
|
|
|
|
if (pi->pt_anything || !pi->is_dereferenced)
|
|
{
|
|
/* No name tags for pointers that have not been
|
|
dereferenced or point to an arbitrary location. */
|
|
pi->name_mem_tag = NULL_TREE;
|
|
continue;
|
|
}
|
|
|
|
if (pi->pt_vars && !bitmap_empty_p (pi->pt_vars))
|
|
{
|
|
size_t j;
|
|
tree old_name_tag = pi->name_mem_tag;
|
|
|
|
/* If PTR points to a set of variables, check if we don't
|
|
have another pointer Q with the same points-to set before
|
|
creating a tag. If so, use Q's tag instead of creating a
|
|
new one.
|
|
|
|
This is important for not creating unnecessary symbols
|
|
and also for copy propagation. If we ever need to
|
|
propagate PTR into Q or vice-versa, we would run into
|
|
problems if they both had different name tags because
|
|
they would have different SSA version numbers (which
|
|
would force us to take the name tags in and out of SSA). */
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
tree q = VARRAY_TREE (ai->processed_ptrs, j);
|
|
struct ptr_info_def *qi = SSA_NAME_PTR_INFO (q);
|
|
|
|
if (qi
|
|
&& qi->pt_vars
|
|
&& qi->name_mem_tag
|
|
&& bitmap_equal_p (pi->pt_vars, qi->pt_vars))
|
|
{
|
|
pi->name_mem_tag = qi->name_mem_tag;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If we didn't find a pointer with the same points-to set
|
|
as PTR, create a new name tag if needed. */
|
|
if (pi->name_mem_tag == NULL_TREE)
|
|
pi->name_mem_tag = get_nmt_for (ptr);
|
|
|
|
/* If the new name tag computed for PTR is different than
|
|
the old name tag that it used to have, then the old tag
|
|
needs to be removed from the IL, so we mark it for
|
|
renaming. */
|
|
if (old_name_tag && old_name_tag != pi->name_mem_tag)
|
|
bitmap_set_bit (vars_to_rename, var_ann (old_name_tag)->uid);
|
|
}
|
|
else if (pi->pt_malloc)
|
|
{
|
|
/* Otherwise, create a unique name tag for this pointer. */
|
|
pi->name_mem_tag = get_nmt_for (ptr);
|
|
}
|
|
else
|
|
{
|
|
/* Only pointers that may point to malloc or other variables
|
|
may receive a name tag. If the pointer does not point to
|
|
a known spot, we should use type tags. */
|
|
set_pt_anything (ptr);
|
|
continue;
|
|
}
|
|
|
|
TREE_THIS_VOLATILE (pi->name_mem_tag)
|
|
|= TREE_THIS_VOLATILE (TREE_TYPE (TREE_TYPE (ptr)));
|
|
|
|
/* Mark the new name tag for renaming. */
|
|
bitmap_set_bit (vars_to_rename, var_ann (pi->name_mem_tag)->uid);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* For every pointer P_i in AI->PROCESSED_PTRS, create may-alias sets for
|
|
the name memory tag (NMT) associated with P_i. If P_i escapes, then its
|
|
name tag and the variables it points-to are call-clobbered. Finally, if
|
|
P_i escapes and we could not determine where it points to, then all the
|
|
variables in the same alias set as *P_i are marked call-clobbered. This
|
|
is necessary because we must assume that P_i may take the address of any
|
|
variable in the same alias set. */
|
|
|
|
static void
|
|
compute_flow_sensitive_aliasing (struct alias_info *ai)
|
|
{
|
|
size_t i;
|
|
|
|
create_name_tags (ai);
|
|
|
|
for (i = 0; i < VARRAY_ACTIVE_SIZE (ai->processed_ptrs); i++)
|
|
{
|
|
unsigned j;
|
|
tree ptr = VARRAY_TREE (ai->processed_ptrs, i);
|
|
struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
|
|
var_ann_t v_ann = var_ann (SSA_NAME_VAR (ptr));
|
|
bitmap_iterator bi;
|
|
|
|
if (pi->value_escapes_p || pi->pt_anything)
|
|
{
|
|
/* If PTR escapes or may point to anything, then its associated
|
|
memory tags and pointed-to variables are call-clobbered. */
|
|
if (pi->name_mem_tag)
|
|
mark_call_clobbered (pi->name_mem_tag);
|
|
|
|
if (v_ann->type_mem_tag)
|
|
mark_call_clobbered (v_ann->type_mem_tag);
|
|
|
|
if (pi->pt_vars)
|
|
EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j, bi)
|
|
{
|
|
mark_call_clobbered (referenced_var (j));
|
|
}
|
|
}
|
|
|
|
/* Set up aliasing information for PTR's name memory tag (if it has
|
|
one). Note that only pointers that have been dereferenced will
|
|
have a name memory tag. */
|
|
if (pi->name_mem_tag && pi->pt_vars)
|
|
EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j, bi)
|
|
{
|
|
add_may_alias (pi->name_mem_tag, referenced_var (j));
|
|
}
|
|
|
|
/* If the name tag is call clobbered, so is the type tag
|
|
associated with the base VAR_DECL. */
|
|
if (pi->name_mem_tag
|
|
&& v_ann->type_mem_tag
|
|
&& is_call_clobbered (pi->name_mem_tag))
|
|
mark_call_clobbered (v_ann->type_mem_tag);
|
|
}
|
|
}
|
|
|
|
|
|
/* Compute type-based alias sets. Traverse all the pointers and
|
|
addressable variables found in setup_pointers_and_addressables.
|
|
|
|
For every pointer P in AI->POINTERS and addressable variable V in
|
|
AI->ADDRESSABLE_VARS, add V to the may-alias sets of P's type
|
|
memory tag (TMT) if their alias sets conflict. V is then marked as
|
|
an alias tag so that the operand scanner knows that statements
|
|
containing V have aliased operands. */
|
|
|
|
static void
|
|
compute_flow_insensitive_aliasing (struct alias_info *ai)
|
|
{
|
|
size_t i;
|
|
|
|
/* Initialize counter for the total number of virtual operands that
|
|
aliasing will introduce. When AI->TOTAL_ALIAS_VOPS goes beyond the
|
|
threshold set by --params max-alias-vops, we enable alias
|
|
grouping. */
|
|
ai->total_alias_vops = 0;
|
|
|
|
/* For every pointer P, determine which addressable variables may alias
|
|
with P's type memory tag. */
|
|
for (i = 0; i < ai->num_pointers; i++)
|
|
{
|
|
size_t j;
|
|
struct alias_map_d *p_map = ai->pointers[i];
|
|
tree tag = var_ann (p_map->var)->type_mem_tag;
|
|
var_ann_t tag_ann = var_ann (tag);
|
|
|
|
p_map->total_alias_vops = 0;
|
|
p_map->may_aliases = sbitmap_alloc (num_referenced_vars);
|
|
sbitmap_zero (p_map->may_aliases);
|
|
|
|
for (j = 0; j < ai->num_addressable_vars; j++)
|
|
{
|
|
struct alias_map_d *v_map;
|
|
var_ann_t v_ann;
|
|
tree var;
|
|
bool tag_stored_p, var_stored_p;
|
|
|
|
v_map = ai->addressable_vars[j];
|
|
var = v_map->var;
|
|
v_ann = var_ann (var);
|
|
|
|
/* Skip memory tags and variables that have never been
|
|
written to. We also need to check if the variables are
|
|
call-clobbered because they may be overwritten by
|
|
function calls.
|
|
|
|
Note this is effectively random accessing elements in
|
|
the sparse bitset, which can be highly inefficient.
|
|
So we first check the call_clobbered status of the
|
|
tag and variable before querying the bitmap. */
|
|
tag_stored_p = is_call_clobbered (tag)
|
|
|| bitmap_bit_p (ai->written_vars, tag_ann->uid);
|
|
var_stored_p = is_call_clobbered (var)
|
|
|| bitmap_bit_p (ai->written_vars, v_ann->uid);
|
|
if (!tag_stored_p && !var_stored_p)
|
|
continue;
|
|
|
|
if (may_alias_p (p_map->var, p_map->set, var, v_map->set))
|
|
{
|
|
size_t num_tag_refs, num_var_refs;
|
|
|
|
num_tag_refs = VARRAY_UINT (ai->num_references, tag_ann->uid);
|
|
num_var_refs = VARRAY_UINT (ai->num_references, v_ann->uid);
|
|
|
|
/* Add VAR to TAG's may-aliases set. */
|
|
add_may_alias (tag, var);
|
|
|
|
/* Update the total number of virtual operands due to
|
|
aliasing. Since we are adding one more alias to TAG's
|
|
may-aliases set, the total number of virtual operands due
|
|
to aliasing will be increased by the number of references
|
|
made to VAR and TAG (every reference to TAG will also
|
|
count as a reference to VAR). */
|
|
ai->total_alias_vops += (num_var_refs + num_tag_refs);
|
|
p_map->total_alias_vops += (num_var_refs + num_tag_refs);
|
|
|
|
/* Update the bitmap used to represent TAG's alias set
|
|
in case we need to group aliases. */
|
|
SET_BIT (p_map->may_aliases, var_ann (var)->uid);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Since this analysis is based exclusively on symbols, it fails to
|
|
handle cases where two pointers P and Q have different memory
|
|
tags with conflicting alias set numbers but no aliased symbols in
|
|
common.
|
|
|
|
For example, suppose that we have two memory tags TMT.1 and TMT.2
|
|
such that
|
|
|
|
may-aliases (TMT.1) = { a }
|
|
may-aliases (TMT.2) = { b }
|
|
|
|
and the alias set number of TMT.1 conflicts with that of TMT.2.
|
|
Since they don't have symbols in common, loads and stores from
|
|
TMT.1 and TMT.2 will seem independent of each other, which will
|
|
lead to the optimizers making invalid transformations (see
|
|
testsuite/gcc.c-torture/execute/pr15262-[12].c).
|
|
|
|
To avoid this problem, we do a final traversal of AI->POINTERS
|
|
looking for pairs of pointers that have no aliased symbols in
|
|
common and yet have conflicting alias set numbers. */
|
|
for (i = 0; i < ai->num_pointers; i++)
|
|
{
|
|
size_t j;
|
|
struct alias_map_d *p_map1 = ai->pointers[i];
|
|
tree tag1 = var_ann (p_map1->var)->type_mem_tag;
|
|
sbitmap may_aliases1 = p_map1->may_aliases;
|
|
|
|
for (j = i + 1; j < ai->num_pointers; j++)
|
|
{
|
|
struct alias_map_d *p_map2 = ai->pointers[j];
|
|
tree tag2 = var_ann (p_map2->var)->type_mem_tag;
|
|
sbitmap may_aliases2 = p_map2->may_aliases;
|
|
|
|
/* If the pointers may not point to each other, do nothing. */
|
|
if (!may_alias_p (p_map1->var, p_map1->set, p_map2->var, p_map2->set))
|
|
continue;
|
|
|
|
/* The two pointers may alias each other. If they already have
|
|
symbols in common, do nothing. */
|
|
if (sbitmap_any_common_bits (may_aliases1, may_aliases2))
|
|
continue;
|
|
|
|
if (sbitmap_first_set_bit (may_aliases2) >= 0)
|
|
{
|
|
size_t k;
|
|
|
|
/* Add all the aliases for TAG2 into TAG1's alias set.
|
|
FIXME, update grouping heuristic counters. */
|
|
EXECUTE_IF_SET_IN_SBITMAP (may_aliases2, 0, k,
|
|
add_may_alias (tag1, referenced_var (k)));
|
|
sbitmap_a_or_b (may_aliases1, may_aliases1, may_aliases2);
|
|
}
|
|
else
|
|
{
|
|
/* Since TAG2 does not have any aliases of its own, add
|
|
TAG2 itself to the alias set of TAG1. */
|
|
add_may_alias (tag1, tag2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dump_file)
|
|
fprintf (dump_file, "%s: Total number of aliased vops: %ld\n",
|
|
get_name (current_function_decl),
|
|
ai->total_alias_vops);
|
|
|
|
/* Determine if we need to enable alias grouping. */
|
|
if (ai->total_alias_vops >= MAX_ALIASED_VOPS)
|
|
group_aliases (ai);
|
|
}
|
|
|
|
|
|
/* Comparison function for qsort used in group_aliases. */
|
|
|
|
static int
|
|
total_alias_vops_cmp (const void *p, const void *q)
|
|
{
|
|
const struct alias_map_d **p1 = (const struct alias_map_d **)p;
|
|
const struct alias_map_d **p2 = (const struct alias_map_d **)q;
|
|
long n1 = (*p1)->total_alias_vops;
|
|
long n2 = (*p2)->total_alias_vops;
|
|
|
|
/* We want to sort in descending order. */
|
|
return (n1 > n2 ? -1 : (n1 == n2) ? 0 : 1);
|
|
}
|
|
|
|
/* Group all the aliases for TAG to make TAG represent all the
|
|
variables in its alias set. Update the total number
|
|
of virtual operands due to aliasing (AI->TOTAL_ALIAS_VOPS). This
|
|
function will make TAG be the unique alias tag for all the
|
|
variables in its may-aliases. So, given:
|
|
|
|
may-aliases(TAG) = { V1, V2, V3 }
|
|
|
|
This function will group the variables into:
|
|
|
|
may-aliases(V1) = { TAG }
|
|
may-aliases(V2) = { TAG }
|
|
may-aliases(V2) = { TAG } */
|
|
|
|
static void
|
|
group_aliases_into (tree tag, sbitmap tag_aliases, struct alias_info *ai)
|
|
{
|
|
size_t i;
|
|
var_ann_t tag_ann = var_ann (tag);
|
|
size_t num_tag_refs = VARRAY_UINT (ai->num_references, tag_ann->uid);
|
|
|
|
EXECUTE_IF_SET_IN_SBITMAP (tag_aliases, 0, i,
|
|
{
|
|
tree var = referenced_var (i);
|
|
var_ann_t ann = var_ann (var);
|
|
|
|
/* Make TAG the unique alias of VAR. */
|
|
ann->is_alias_tag = 0;
|
|
ann->may_aliases = NULL;
|
|
|
|
/* Note that VAR and TAG may be the same if the function has no
|
|
addressable variables (see the discussion at the end of
|
|
setup_pointers_and_addressables). */
|
|
if (var != tag)
|
|
add_may_alias (var, tag);
|
|
|
|
/* Reduce total number of virtual operands contributed
|
|
by TAG on behalf of VAR. Notice that the references to VAR
|
|
itself won't be removed. We will merely replace them with
|
|
references to TAG. */
|
|
ai->total_alias_vops -= num_tag_refs;
|
|
});
|
|
|
|
/* We have reduced the number of virtual operands that TAG makes on
|
|
behalf of all the variables formerly aliased with it. However,
|
|
we have also "removed" all the virtual operands for TAG itself,
|
|
so we add them back. */
|
|
ai->total_alias_vops += num_tag_refs;
|
|
|
|
/* TAG no longer has any aliases. */
|
|
tag_ann->may_aliases = NULL;
|
|
}
|
|
|
|
|
|
/* Group may-aliases sets to reduce the number of virtual operands due
|
|
to aliasing.
|
|
|
|
1- Sort the list of pointers in decreasing number of contributed
|
|
virtual operands.
|
|
|
|
2- Take the first entry in AI->POINTERS and revert the role of
|
|
the memory tag and its aliases. Usually, whenever an aliased
|
|
variable Vi is found to alias with a memory tag T, we add Vi
|
|
to the may-aliases set for T. Meaning that after alias
|
|
analysis, we will have:
|
|
|
|
may-aliases(T) = { V1, V2, V3, ..., Vn }
|
|
|
|
This means that every statement that references T, will get 'n'
|
|
virtual operands for each of the Vi tags. But, when alias
|
|
grouping is enabled, we make T an alias tag and add it to the
|
|
alias set of all the Vi variables:
|
|
|
|
may-aliases(V1) = { T }
|
|
may-aliases(V2) = { T }
|
|
...
|
|
may-aliases(Vn) = { T }
|
|
|
|
This has two effects: (a) statements referencing T will only get
|
|
a single virtual operand, and, (b) all the variables Vi will now
|
|
appear to alias each other. So, we lose alias precision to
|
|
improve compile time. But, in theory, a program with such a high
|
|
level of aliasing should not be very optimizable in the first
|
|
place.
|
|
|
|
3- Since variables may be in the alias set of more than one
|
|
memory tag, the grouping done in step (2) needs to be extended
|
|
to all the memory tags that have a non-empty intersection with
|
|
the may-aliases set of tag T. For instance, if we originally
|
|
had these may-aliases sets:
|
|
|
|
may-aliases(T) = { V1, V2, V3 }
|
|
may-aliases(R) = { V2, V4 }
|
|
|
|
In step (2) we would have reverted the aliases for T as:
|
|
|
|
may-aliases(V1) = { T }
|
|
may-aliases(V2) = { T }
|
|
may-aliases(V3) = { T }
|
|
|
|
But note that now V2 is no longer aliased with R. We could
|
|
add R to may-aliases(V2), but we are in the process of
|
|
grouping aliases to reduce virtual operands so what we do is
|
|
add V4 to the grouping to obtain:
|
|
|
|
may-aliases(V1) = { T }
|
|
may-aliases(V2) = { T }
|
|
may-aliases(V3) = { T }
|
|
may-aliases(V4) = { T }
|
|
|
|
4- If the total number of virtual operands due to aliasing is
|
|
still above the threshold set by max-alias-vops, go back to (2). */
|
|
|
|
static void
|
|
group_aliases (struct alias_info *ai)
|
|
{
|
|
size_t i;
|
|
|
|
/* Sort the POINTERS array in descending order of contributed
|
|
virtual operands. */
|
|
qsort (ai->pointers, ai->num_pointers, sizeof (struct alias_map_d *),
|
|
total_alias_vops_cmp);
|
|
|
|
/* For every pointer in AI->POINTERS, reverse the roles of its tag
|
|
and the tag's may-aliases set. */
|
|
for (i = 0; i < ai->num_pointers; i++)
|
|
{
|
|
size_t j;
|
|
tree tag1 = var_ann (ai->pointers[i]->var)->type_mem_tag;
|
|
sbitmap tag1_aliases = ai->pointers[i]->may_aliases;
|
|
|
|
/* Skip tags that have been grouped already. */
|
|
if (ai->pointers[i]->grouped_p)
|
|
continue;
|
|
|
|
/* See if TAG1 had any aliases in common with other type tags.
|
|
If we find a TAG2 with common aliases with TAG1, add TAG2's
|
|
aliases into TAG1. */
|
|
for (j = i + 1; j < ai->num_pointers; j++)
|
|
{
|
|
sbitmap tag2_aliases = ai->pointers[j]->may_aliases;
|
|
|
|
if (sbitmap_any_common_bits (tag1_aliases, tag2_aliases))
|
|
{
|
|
tree tag2 = var_ann (ai->pointers[j]->var)->type_mem_tag;
|
|
|
|
sbitmap_a_or_b (tag1_aliases, tag1_aliases, tag2_aliases);
|
|
|
|
/* TAG2 does not need its aliases anymore. */
|
|
sbitmap_zero (tag2_aliases);
|
|
var_ann (tag2)->may_aliases = NULL;
|
|
|
|
/* TAG1 is the unique alias of TAG2. */
|
|
add_may_alias (tag2, tag1);
|
|
|
|
ai->pointers[j]->grouped_p = true;
|
|
}
|
|
}
|
|
|
|
/* Now group all the aliases we collected into TAG1. */
|
|
group_aliases_into (tag1, tag1_aliases, ai);
|
|
|
|
/* If we've reduced total number of virtual operands below the
|
|
threshold, stop. */
|
|
if (ai->total_alias_vops < MAX_ALIASED_VOPS)
|
|
break;
|
|
}
|
|
|
|
/* Finally, all the variables that have been grouped cannot be in
|
|
the may-alias set of name memory tags. Suppose that we have
|
|
grouped the aliases in this code so that may-aliases(a) = TMT.20
|
|
|
|
p_5 = &a;
|
|
...
|
|
# a_9 = V_MAY_DEF <a_8>
|
|
p_5->field = 0
|
|
... Several modifications to TMT.20 ...
|
|
# VUSE <a_9>
|
|
x_30 = p_5->field
|
|
|
|
Since p_5 points to 'a', the optimizers will try to propagate 0
|
|
into p_5->field, but that is wrong because there have been
|
|
modifications to 'TMT.20' in between. To prevent this we have to
|
|
replace 'a' with 'TMT.20' in the name tag of p_5. */
|
|
for (i = 0; i < VARRAY_ACTIVE_SIZE (ai->processed_ptrs); i++)
|
|
{
|
|
size_t j;
|
|
tree ptr = VARRAY_TREE (ai->processed_ptrs, i);
|
|
tree name_tag = SSA_NAME_PTR_INFO (ptr)->name_mem_tag;
|
|
varray_type aliases;
|
|
|
|
if (name_tag == NULL_TREE)
|
|
continue;
|
|
|
|
aliases = var_ann (name_tag)->may_aliases;
|
|
for (j = 0; aliases && j < VARRAY_ACTIVE_SIZE (aliases); j++)
|
|
{
|
|
tree alias = VARRAY_TREE (aliases, j);
|
|
var_ann_t ann = var_ann (alias);
|
|
|
|
if (ann->mem_tag_kind == NOT_A_TAG && ann->may_aliases)
|
|
{
|
|
tree new_alias;
|
|
|
|
gcc_assert (VARRAY_ACTIVE_SIZE (ann->may_aliases) == 1);
|
|
|
|
new_alias = VARRAY_TREE (ann->may_aliases, 0);
|
|
replace_may_alias (name_tag, j, new_alias);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dump_file)
|
|
fprintf (dump_file,
|
|
"%s: Total number of aliased vops after grouping: %ld%s\n",
|
|
get_name (current_function_decl),
|
|
ai->total_alias_vops,
|
|
(ai->total_alias_vops < 0) ? " (negative values are OK)" : "");
|
|
}
|
|
|
|
|
|
/* Create a new alias set entry for VAR in AI->ADDRESSABLE_VARS. */
|
|
|
|
static void
|
|
create_alias_map_for (tree var, struct alias_info *ai)
|
|
{
|
|
struct alias_map_d *alias_map;
|
|
alias_map = xcalloc (1, sizeof (*alias_map));
|
|
alias_map->var = var;
|
|
alias_map->set = get_alias_set (var);
|
|
ai->addressable_vars[ai->num_addressable_vars++] = alias_map;
|
|
}
|
|
|
|
|
|
/* Create memory tags for all the dereferenced pointers and build the
|
|
ADDRESSABLE_VARS and POINTERS arrays used for building the may-alias
|
|
sets. Based on the address escape and points-to information collected
|
|
earlier, this pass will also clear the TREE_ADDRESSABLE flag from those
|
|
variables whose address is not needed anymore. */
|
|
|
|
static void
|
|
setup_pointers_and_addressables (struct alias_info *ai)
|
|
{
|
|
size_t i, n_vars, num_addressable_vars, num_pointers;
|
|
|
|
/* Size up the arrays ADDRESSABLE_VARS and POINTERS. */
|
|
num_addressable_vars = num_pointers = 0;
|
|
for (i = 0; i < num_referenced_vars; i++)
|
|
{
|
|
tree var = referenced_var (i);
|
|
|
|
if (may_be_aliased (var))
|
|
num_addressable_vars++;
|
|
|
|
if (POINTER_TYPE_P (TREE_TYPE (var)))
|
|
{
|
|
/* Since we don't keep track of volatile variables, assume that
|
|
these pointers are used in indirect store operations. */
|
|
if (TREE_THIS_VOLATILE (var))
|
|
bitmap_set_bit (ai->dereferenced_ptrs_store, var_ann (var)->uid);
|
|
|
|
num_pointers++;
|
|
}
|
|
}
|
|
|
|
/* Create ADDRESSABLE_VARS and POINTERS. Note that these arrays are
|
|
always going to be slightly bigger than we actually need them
|
|
because some TREE_ADDRESSABLE variables will be marked
|
|
non-addressable below and only pointers with unique type tags are
|
|
going to be added to POINTERS. */
|
|
ai->addressable_vars = xcalloc (num_addressable_vars,
|
|
sizeof (struct alias_map_d *));
|
|
ai->pointers = xcalloc (num_pointers, sizeof (struct alias_map_d *));
|
|
ai->num_addressable_vars = 0;
|
|
ai->num_pointers = 0;
|
|
|
|
/* Since we will be creating type memory tags within this loop, cache the
|
|
value of NUM_REFERENCED_VARS to avoid processing the additional tags
|
|
unnecessarily. */
|
|
n_vars = num_referenced_vars;
|
|
|
|
for (i = 0; i < n_vars; i++)
|
|
{
|
|
tree var = referenced_var (i);
|
|
var_ann_t v_ann = var_ann (var);
|
|
|
|
/* Name memory tags already have flow-sensitive aliasing
|
|
information, so they need not be processed by
|
|
compute_flow_insensitive_aliasing. Similarly, type memory
|
|
tags are already accounted for when we process their
|
|
associated pointer. */
|
|
if (v_ann->mem_tag_kind != NOT_A_TAG)
|
|
continue;
|
|
|
|
/* Remove the ADDRESSABLE flag from every addressable variable whose
|
|
address is not needed anymore. This is caused by the propagation
|
|
of ADDR_EXPR constants into INDIRECT_REF expressions and the
|
|
removal of dead pointer assignments done by the early scalar
|
|
cleanup passes. */
|
|
if (TREE_ADDRESSABLE (var))
|
|
{
|
|
if (!bitmap_bit_p (ai->addresses_needed, v_ann->uid)
|
|
&& TREE_CODE (var) != RESULT_DECL
|
|
&& !is_global_var (var))
|
|
{
|
|
/* The address of VAR is not needed, remove the
|
|
addressable bit, so that it can be optimized as a
|
|
regular variable. */
|
|
mark_non_addressable (var);
|
|
|
|
/* Since VAR is now a regular GIMPLE register, we will need
|
|
to rename VAR into SSA afterwards. */
|
|
bitmap_set_bit (vars_to_rename, v_ann->uid);
|
|
}
|
|
else
|
|
{
|
|
/* Add the variable to the set of addressables. Mostly
|
|
used when scanning operands for ASM_EXPRs that
|
|
clobber memory. In those cases, we need to clobber
|
|
all call-clobbered variables and all addressables. */
|
|
bitmap_set_bit (addressable_vars, v_ann->uid);
|
|
}
|
|
}
|
|
|
|
/* Global variables and addressable locals may be aliased. Create an
|
|
entry in ADDRESSABLE_VARS for VAR. */
|
|
if (may_be_aliased (var))
|
|
{
|
|
create_alias_map_for (var, ai);
|
|
bitmap_set_bit (vars_to_rename, var_ann (var)->uid);
|
|
}
|
|
|
|
/* Add pointer variables that have been dereferenced to the POINTERS
|
|
array and create a type memory tag for them. */
|
|
if (POINTER_TYPE_P (TREE_TYPE (var)))
|
|
{
|
|
if ((bitmap_bit_p (ai->dereferenced_ptrs_store, v_ann->uid)
|
|
|| bitmap_bit_p (ai->dereferenced_ptrs_load, v_ann->uid)))
|
|
{
|
|
tree tag;
|
|
var_ann_t t_ann;
|
|
|
|
/* If pointer VAR still doesn't have a memory tag
|
|
associated with it, create it now or re-use an
|
|
existing one. */
|
|
tag = get_tmt_for (var, ai);
|
|
t_ann = var_ann (tag);
|
|
|
|
/* The type tag will need to be renamed into SSA
|
|
afterwards. Note that we cannot do this inside
|
|
get_tmt_for because aliasing may run multiple times
|
|
and we only create type tags the first time. */
|
|
bitmap_set_bit (vars_to_rename, t_ann->uid);
|
|
|
|
/* Associate the tag with pointer VAR. */
|
|
v_ann->type_mem_tag = tag;
|
|
|
|
/* If pointer VAR has been used in a store operation,
|
|
then its memory tag must be marked as written-to. */
|
|
if (bitmap_bit_p (ai->dereferenced_ptrs_store, v_ann->uid))
|
|
bitmap_set_bit (ai->written_vars, t_ann->uid);
|
|
|
|
/* If pointer VAR is a global variable or a PARM_DECL,
|
|
then its memory tag should be considered a global
|
|
variable. */
|
|
if (TREE_CODE (var) == PARM_DECL || is_global_var (var))
|
|
mark_call_clobbered (tag);
|
|
|
|
/* All the dereferences of pointer VAR count as
|
|
references of TAG. Since TAG can be associated with
|
|
several pointers, add the dereferences of VAR to the
|
|
TAG. We may need to grow AI->NUM_REFERENCES because
|
|
we have been adding name and type tags. */
|
|
if (t_ann->uid >= VARRAY_SIZE (ai->num_references))
|
|
VARRAY_GROW (ai->num_references, t_ann->uid + 10);
|
|
|
|
VARRAY_UINT (ai->num_references, t_ann->uid)
|
|
+= VARRAY_UINT (ai->num_references, v_ann->uid);
|
|
}
|
|
else
|
|
{
|
|
/* The pointer has not been dereferenced. If it had a
|
|
type memory tag, remove it and mark the old tag for
|
|
renaming to remove it out of the IL. */
|
|
var_ann_t ann = var_ann (var);
|
|
tree tag = ann->type_mem_tag;
|
|
if (tag)
|
|
{
|
|
bitmap_set_bit (vars_to_rename, var_ann (tag)->uid);
|
|
ann->type_mem_tag = NULL_TREE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Determine whether to use .GLOBAL_VAR to model call clobbering semantics. At
|
|
every call site, we need to emit V_MAY_DEF expressions to represent the
|
|
clobbering effects of the call for variables whose address escapes the
|
|
current function.
|
|
|
|
One approach is to group all call-clobbered variables into a single
|
|
representative that is used as an alias of every call-clobbered variable
|
|
(.GLOBAL_VAR). This works well, but it ties the optimizer hands because
|
|
references to any call clobbered variable is a reference to .GLOBAL_VAR.
|
|
|
|
The second approach is to emit a clobbering V_MAY_DEF for every
|
|
call-clobbered variable at call sites. This is the preferred way in terms
|
|
of optimization opportunities but it may create too many V_MAY_DEF operands
|
|
if there are many call clobbered variables and function calls in the
|
|
function.
|
|
|
|
To decide whether or not to use .GLOBAL_VAR we multiply the number of
|
|
function calls found by the number of call-clobbered variables. If that
|
|
product is beyond a certain threshold, as determined by the parameterized
|
|
values shown below, we use .GLOBAL_VAR.
|
|
|
|
FIXME. This heuristic should be improved. One idea is to use several
|
|
.GLOBAL_VARs of different types instead of a single one. The thresholds
|
|
have been derived from a typical bootstrap cycle, including all target
|
|
libraries. Compile times were found increase by ~1% compared to using
|
|
.GLOBAL_VAR. */
|
|
|
|
static void
|
|
maybe_create_global_var (struct alias_info *ai)
|
|
{
|
|
unsigned i, n_clobbered;
|
|
bitmap_iterator bi;
|
|
|
|
/* No need to create it, if we have one already. */
|
|
if (global_var == NULL_TREE)
|
|
{
|
|
/* Count all the call-clobbered variables. */
|
|
n_clobbered = 0;
|
|
EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
|
|
{
|
|
n_clobbered++;
|
|
}
|
|
|
|
/* Create .GLOBAL_VAR if we have too many call-clobbered
|
|
variables. We also create .GLOBAL_VAR when there no
|
|
call-clobbered variables to prevent code motion
|
|
transformations from re-arranging function calls that may
|
|
have side effects. For instance,
|
|
|
|
foo ()
|
|
{
|
|
int a = f ();
|
|
g ();
|
|
h (a);
|
|
}
|
|
|
|
There are no call-clobbered variables in foo(), so it would
|
|
be entirely possible for a pass to want to move the call to
|
|
f() after the call to g(). If f() has side effects, that
|
|
would be wrong. Creating .GLOBAL_VAR in this case will
|
|
insert VDEFs for it and prevent such transformations. */
|
|
if (n_clobbered == 0
|
|
|| ai->num_calls_found * n_clobbered >= (size_t) GLOBAL_VAR_THRESHOLD)
|
|
create_global_var ();
|
|
}
|
|
|
|
/* If the function has calls to clobbering functions and .GLOBAL_VAR has
|
|
been created, make it an alias for all call-clobbered variables. */
|
|
if (global_var)
|
|
EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
|
|
{
|
|
tree var = referenced_var (i);
|
|
if (var != global_var)
|
|
{
|
|
add_may_alias (var, global_var);
|
|
bitmap_set_bit (vars_to_rename, var_ann (var)->uid);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Return TRUE if pointer PTR may point to variable VAR.
|
|
|
|
MEM_ALIAS_SET is the alias set for the memory location pointed-to by PTR
|
|
This is needed because when checking for type conflicts we are
|
|
interested in the alias set of the memory location pointed-to by
|
|
PTR. The alias set of PTR itself is irrelevant.
|
|
|
|
VAR_ALIAS_SET is the alias set for VAR. */
|
|
|
|
static bool
|
|
may_alias_p (tree ptr, HOST_WIDE_INT mem_alias_set,
|
|
tree var, HOST_WIDE_INT var_alias_set)
|
|
{
|
|
tree mem;
|
|
var_ann_t v_ann, m_ann;
|
|
|
|
alias_stats.alias_queries++;
|
|
alias_stats.simple_queries++;
|
|
|
|
/* By convention, a variable cannot alias itself. */
|
|
mem = var_ann (ptr)->type_mem_tag;
|
|
if (mem == var)
|
|
{
|
|
alias_stats.alias_noalias++;
|
|
alias_stats.simple_resolved++;
|
|
return false;
|
|
}
|
|
|
|
v_ann = var_ann (var);
|
|
m_ann = var_ann (mem);
|
|
|
|
gcc_assert (m_ann->mem_tag_kind == TYPE_TAG);
|
|
|
|
alias_stats.tbaa_queries++;
|
|
|
|
/* If VAR is a pointer with the same alias set as PTR, then dereferencing
|
|
PTR can't possibly affect VAR. Note, that we are specifically testing
|
|
for PTR's alias set here, not its pointed-to type. We also can't
|
|
do this check with relaxed aliasing enabled. */
|
|
if (POINTER_TYPE_P (TREE_TYPE (var))
|
|
&& var_alias_set != 0
|
|
&& mem_alias_set != 0)
|
|
{
|
|
HOST_WIDE_INT ptr_alias_set = get_alias_set (ptr);
|
|
if (ptr_alias_set == var_alias_set)
|
|
{
|
|
alias_stats.alias_noalias++;
|
|
alias_stats.tbaa_resolved++;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* If the alias sets don't conflict then MEM cannot alias VAR. */
|
|
if (!alias_sets_conflict_p (mem_alias_set, var_alias_set))
|
|
{
|
|
alias_stats.alias_noalias++;
|
|
alias_stats.tbaa_resolved++;
|
|
return false;
|
|
}
|
|
|
|
alias_stats.alias_mayalias++;
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Add ALIAS to the set of variables that may alias VAR. */
|
|
|
|
static void
|
|
add_may_alias (tree var, tree alias)
|
|
{
|
|
size_t i;
|
|
var_ann_t v_ann = get_var_ann (var);
|
|
var_ann_t a_ann = get_var_ann (alias);
|
|
|
|
gcc_assert (var != alias);
|
|
|
|
if (v_ann->may_aliases == NULL)
|
|
VARRAY_TREE_INIT (v_ann->may_aliases, 2, "aliases");
|
|
|
|
/* Avoid adding duplicates. */
|
|
for (i = 0; i < VARRAY_ACTIVE_SIZE (v_ann->may_aliases); i++)
|
|
if (alias == VARRAY_TREE (v_ann->may_aliases, i))
|
|
return;
|
|
|
|
/* If VAR is a call-clobbered variable, so is its new ALIAS.
|
|
FIXME, call-clobbering should only depend on whether an address
|
|
escapes. It should be independent of aliasing. */
|
|
if (is_call_clobbered (var))
|
|
mark_call_clobbered (alias);
|
|
|
|
/* Likewise. If ALIAS is call-clobbered, so is VAR. */
|
|
else if (is_call_clobbered (alias))
|
|
mark_call_clobbered (var);
|
|
|
|
VARRAY_PUSH_TREE (v_ann->may_aliases, alias);
|
|
a_ann->is_alias_tag = 1;
|
|
}
|
|
|
|
|
|
/* Replace alias I in the alias sets of VAR with NEW_ALIAS. */
|
|
|
|
static void
|
|
replace_may_alias (tree var, size_t i, tree new_alias)
|
|
{
|
|
var_ann_t v_ann = var_ann (var);
|
|
VARRAY_TREE (v_ann->may_aliases, i) = new_alias;
|
|
|
|
/* If VAR is a call-clobbered variable, so is NEW_ALIAS.
|
|
FIXME, call-clobbering should only depend on whether an address
|
|
escapes. It should be independent of aliasing. */
|
|
if (is_call_clobbered (var))
|
|
mark_call_clobbered (new_alias);
|
|
|
|
/* Likewise. If NEW_ALIAS is call-clobbered, so is VAR. */
|
|
else if (is_call_clobbered (new_alias))
|
|
mark_call_clobbered (var);
|
|
}
|
|
|
|
|
|
/* Mark pointer PTR as pointing to an arbitrary memory location. */
|
|
|
|
static void
|
|
set_pt_anything (tree ptr)
|
|
{
|
|
struct ptr_info_def *pi = get_ptr_info (ptr);
|
|
|
|
pi->pt_anything = 1;
|
|
pi->pt_malloc = 0;
|
|
|
|
/* The pointer used to have a name tag, but we now found it pointing
|
|
to an arbitrary location. The name tag needs to be renamed and
|
|
disassociated from PTR. */
|
|
if (pi->name_mem_tag)
|
|
{
|
|
bitmap_set_bit (vars_to_rename, var_ann (pi->name_mem_tag)->uid);
|
|
pi->name_mem_tag = NULL_TREE;
|
|
}
|
|
}
|
|
|
|
|
|
/* Mark pointer PTR as pointing to a malloc'd memory area. */
|
|
|
|
static void
|
|
set_pt_malloc (tree ptr)
|
|
{
|
|
struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
|
|
|
|
/* If the pointer has already been found to point to arbitrary
|
|
memory locations, it is unsafe to mark it as pointing to malloc. */
|
|
if (pi->pt_anything)
|
|
return;
|
|
|
|
pi->pt_malloc = 1;
|
|
}
|
|
|
|
|
|
/* Given two different pointers DEST and ORIG. Merge the points-to
|
|
information in ORIG into DEST. AI is as in
|
|
collect_points_to_info. */
|
|
|
|
static void
|
|
merge_pointed_to_info (struct alias_info *ai, tree dest, tree orig)
|
|
{
|
|
struct ptr_info_def *dest_pi, *orig_pi;
|
|
|
|
gcc_assert (dest != orig);
|
|
|
|
/* Make sure we have points-to information for ORIG. */
|
|
collect_points_to_info_for (ai, orig);
|
|
|
|
dest_pi = get_ptr_info (dest);
|
|
orig_pi = SSA_NAME_PTR_INFO (orig);
|
|
|
|
if (orig_pi)
|
|
{
|
|
/* Notice that we never merge PT_MALLOC. This attribute is only
|
|
true if the pointer is the result of a malloc() call.
|
|
Otherwise, we can end up in this situation:
|
|
|
|
P_i = malloc ();
|
|
...
|
|
P_j = P_i + X;
|
|
|
|
P_j would be marked as PT_MALLOC, however we currently do not
|
|
handle cases of more than one pointer pointing to the same
|
|
malloc'd area.
|
|
|
|
FIXME: If the merging comes from an expression that preserves
|
|
the PT_MALLOC attribute (copy assignment, address
|
|
arithmetic), we ought to merge PT_MALLOC, but then both
|
|
pointers would end up getting different name tags because
|
|
create_name_tags is not smart enough to determine that the
|
|
two come from the same malloc call. Copy propagation before
|
|
aliasing should cure this. */
|
|
gcc_assert (orig_pi != dest_pi);
|
|
|
|
dest_pi->pt_malloc = 0;
|
|
|
|
if (orig_pi->pt_malloc || orig_pi->pt_anything)
|
|
set_pt_anything (dest);
|
|
|
|
if (!dest_pi->pt_anything
|
|
&& orig_pi->pt_vars
|
|
&& !bitmap_empty_p (orig_pi->pt_vars))
|
|
{
|
|
if (dest_pi->pt_vars == NULL)
|
|
{
|
|
dest_pi->pt_vars = BITMAP_GGC_ALLOC ();
|
|
bitmap_copy (dest_pi->pt_vars, orig_pi->pt_vars);
|
|
}
|
|
else
|
|
bitmap_ior_into (dest_pi->pt_vars, orig_pi->pt_vars);
|
|
}
|
|
}
|
|
else
|
|
set_pt_anything (dest);
|
|
}
|
|
|
|
|
|
/* Add EXPR to the list of expressions pointed-to by PTR. */
|
|
|
|
static void
|
|
add_pointed_to_expr (struct alias_info *ai, tree ptr, tree expr)
|
|
{
|
|
if (TREE_CODE (expr) == WITH_SIZE_EXPR)
|
|
expr = TREE_OPERAND (expr, 0);
|
|
|
|
get_ptr_info (ptr);
|
|
|
|
if (TREE_CODE (expr) == CALL_EXPR
|
|
&& (call_expr_flags (expr) & (ECF_MALLOC | ECF_MAY_BE_ALLOCA)))
|
|
{
|
|
/* If EXPR is a malloc-like call, then the area pointed to PTR
|
|
is guaranteed to not alias with anything else. */
|
|
set_pt_malloc (ptr);
|
|
}
|
|
else if (TREE_CODE (expr) == ADDR_EXPR)
|
|
{
|
|
/* Found P_i = ADDR_EXPR */
|
|
add_pointed_to_var (ai, ptr, expr);
|
|
}
|
|
else if (TREE_CODE (expr) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (expr)))
|
|
{
|
|
/* Found P_i = Q_j. */
|
|
merge_pointed_to_info (ai, ptr, expr);
|
|
}
|
|
else if (TREE_CODE (expr) == PLUS_EXPR || TREE_CODE (expr) == MINUS_EXPR)
|
|
{
|
|
/* Found P_i = PLUS_EXPR or P_i = MINUS_EXPR */
|
|
tree op0 = TREE_OPERAND (expr, 0);
|
|
tree op1 = TREE_OPERAND (expr, 1);
|
|
|
|
/* Both operands may be of pointer type. FIXME: Shouldn't
|
|
we just expect PTR + OFFSET always? */
|
|
if (POINTER_TYPE_P (TREE_TYPE (op0))
|
|
&& TREE_CODE (op0) != INTEGER_CST)
|
|
{
|
|
if (TREE_CODE (op0) == SSA_NAME)
|
|
merge_pointed_to_info (ai, ptr, op0);
|
|
else if (TREE_CODE (op0) == ADDR_EXPR)
|
|
add_pointed_to_var (ai, ptr, op0);
|
|
else
|
|
set_pt_anything (ptr);
|
|
}
|
|
|
|
if (POINTER_TYPE_P (TREE_TYPE (op1))
|
|
&& TREE_CODE (op1) != INTEGER_CST)
|
|
{
|
|
if (TREE_CODE (op1) == SSA_NAME)
|
|
merge_pointed_to_info (ai, ptr, op1);
|
|
else if (TREE_CODE (op1) == ADDR_EXPR)
|
|
add_pointed_to_var (ai, ptr, op1);
|
|
else
|
|
set_pt_anything (ptr);
|
|
}
|
|
|
|
/* Neither operand is a pointer? VAR can be pointing anywhere.
|
|
FIXME: Shouldn't we abort here? If we get here, we found
|
|
PTR = INT_CST + INT_CST, which should not be a valid pointer
|
|
expression. */
|
|
if (!(POINTER_TYPE_P (TREE_TYPE (op0))
|
|
&& TREE_CODE (op0) != INTEGER_CST)
|
|
&& !(POINTER_TYPE_P (TREE_TYPE (op1))
|
|
&& TREE_CODE (op1) != INTEGER_CST))
|
|
set_pt_anything (ptr);
|
|
}
|
|
else
|
|
{
|
|
/* If we can't recognize the expression, assume that PTR may
|
|
point anywhere. */
|
|
set_pt_anything (ptr);
|
|
}
|
|
}
|
|
|
|
|
|
/* If VALUE is of the form &DECL, add DECL to the set of variables
|
|
pointed-to by PTR. Otherwise, add VALUE as a pointed-to expression by
|
|
PTR. AI is as in collect_points_to_info. */
|
|
|
|
static void
|
|
add_pointed_to_var (struct alias_info *ai, tree ptr, tree value)
|
|
{
|
|
struct ptr_info_def *pi = get_ptr_info (ptr);
|
|
tree pt_var;
|
|
size_t uid;
|
|
|
|
gcc_assert (TREE_CODE (value) == ADDR_EXPR);
|
|
|
|
pt_var = TREE_OPERAND (value, 0);
|
|
if (REFERENCE_CLASS_P (pt_var))
|
|
pt_var = get_base_address (pt_var);
|
|
|
|
if (pt_var && SSA_VAR_P (pt_var))
|
|
{
|
|
uid = var_ann (pt_var)->uid;
|
|
bitmap_set_bit (ai->addresses_needed, uid);
|
|
|
|
if (pi->pt_vars == NULL)
|
|
pi->pt_vars = BITMAP_GGC_ALLOC ();
|
|
bitmap_set_bit (pi->pt_vars, uid);
|
|
|
|
/* If the variable is a global, mark the pointer as pointing to
|
|
global memory (which will make its tag a global variable). */
|
|
if (is_global_var (pt_var))
|
|
pi->pt_global_mem = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Callback for walk_use_def_chains to gather points-to information from the
|
|
SSA web.
|
|
|
|
VAR is an SSA variable or a GIMPLE expression.
|
|
|
|
STMT is the statement that generates the SSA variable or, if STMT is a
|
|
PHI_NODE, VAR is one of the PHI arguments.
|
|
|
|
DATA is a pointer to a structure of type ALIAS_INFO. */
|
|
|
|
static bool
|
|
collect_points_to_info_r (tree var, tree stmt, void *data)
|
|
{
|
|
struct alias_info *ai = (struct alias_info *) data;
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "Visiting use-def links for ");
|
|
print_generic_expr (dump_file, var, dump_flags);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
|
|
switch (TREE_CODE (stmt))
|
|
{
|
|
case RETURN_EXPR:
|
|
if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR)
|
|
abort ();
|
|
stmt = TREE_OPERAND (stmt, 0);
|
|
/* FALLTHRU */
|
|
|
|
case MODIFY_EXPR:
|
|
{
|
|
tree rhs = TREE_OPERAND (stmt, 1);
|
|
STRIP_NOPS (rhs);
|
|
add_pointed_to_expr (ai, var, rhs);
|
|
break;
|
|
}
|
|
|
|
case ASM_EXPR:
|
|
/* Pointers defined by __asm__ statements can point anywhere. */
|
|
set_pt_anything (var);
|
|
break;
|
|
|
|
case NOP_EXPR:
|
|
if (IS_EMPTY_STMT (stmt))
|
|
{
|
|
tree decl = SSA_NAME_VAR (var);
|
|
|
|
if (TREE_CODE (decl) == PARM_DECL)
|
|
add_pointed_to_expr (ai, var, decl);
|
|
else if (DECL_INITIAL (decl))
|
|
add_pointed_to_expr (ai, var, DECL_INITIAL (decl));
|
|
else
|
|
add_pointed_to_expr (ai, var, decl);
|
|
}
|
|
break;
|
|
|
|
case PHI_NODE:
|
|
{
|
|
/* It STMT is a PHI node, then VAR is one of its arguments. The
|
|
variable that we are analyzing is the LHS of the PHI node. */
|
|
tree lhs = PHI_RESULT (stmt);
|
|
|
|
switch (TREE_CODE (var))
|
|
{
|
|
case ADDR_EXPR:
|
|
add_pointed_to_var (ai, lhs, var);
|
|
break;
|
|
|
|
case SSA_NAME:
|
|
/* Avoid unnecessary merges. */
|
|
if (lhs != var)
|
|
merge_pointed_to_info (ai, lhs, var);
|
|
break;
|
|
|
|
default:
|
|
gcc_assert (is_gimple_min_invariant (var));
|
|
add_pointed_to_expr (ai, lhs, var);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
gcc_unreachable ();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Return true if STMT is an "escape" site from the current function. Escape
|
|
sites those statements which might expose the address of a variable
|
|
outside the current function. STMT is an escape site iff:
|
|
|
|
1- STMT is a function call, or
|
|
2- STMT is an __asm__ expression, or
|
|
3- STMT is an assignment to a non-local variable, or
|
|
4- STMT is a return statement.
|
|
|
|
If NUM_CALLS_P is not NULL, the counter is incremented if STMT contains
|
|
a function call. */
|
|
|
|
static bool
|
|
is_escape_site (tree stmt, size_t *num_calls_p)
|
|
{
|
|
if (get_call_expr_in (stmt) != NULL_TREE)
|
|
{
|
|
if (num_calls_p)
|
|
(*num_calls_p)++;
|
|
|
|
return true;
|
|
}
|
|
else if (TREE_CODE (stmt) == ASM_EXPR)
|
|
return true;
|
|
else if (TREE_CODE (stmt) == MODIFY_EXPR)
|
|
{
|
|
tree lhs = TREE_OPERAND (stmt, 0);
|
|
|
|
/* Get to the base of _REF nodes. */
|
|
if (TREE_CODE (lhs) != SSA_NAME)
|
|
lhs = get_base_address (lhs);
|
|
|
|
/* If we couldn't recognize the LHS of the assignment, assume that it
|
|
is a non-local store. */
|
|
if (lhs == NULL_TREE)
|
|
return true;
|
|
|
|
/* If the RHS is a conversion between a pointer and an integer, the
|
|
pointer escapes since we can't track the integer. */
|
|
if ((TREE_CODE (TREE_OPERAND (stmt, 1)) == NOP_EXPR
|
|
|| TREE_CODE (TREE_OPERAND (stmt, 1)) == CONVERT_EXPR
|
|
|| TREE_CODE (TREE_OPERAND (stmt, 1)) == VIEW_CONVERT_EXPR)
|
|
&& POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND
|
|
(TREE_OPERAND (stmt, 1), 0)))
|
|
&& !POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (stmt, 1))))
|
|
return true;
|
|
|
|
/* If the LHS is an SSA name, it can't possibly represent a non-local
|
|
memory store. */
|
|
if (TREE_CODE (lhs) == SSA_NAME)
|
|
return false;
|
|
|
|
/* FIXME: LHS is not an SSA_NAME. Even if it's an assignment to a
|
|
local variables we cannot be sure if it will escape, because we
|
|
don't have information about objects not in SSA form. Need to
|
|
implement something along the lines of
|
|
|
|
J.-D. Choi, M. Gupta, M. J. Serrano, V. C. Sreedhar, and S. P.
|
|
Midkiff, ``Escape analysis for java,'' in Proceedings of the
|
|
Conference on Object-Oriented Programming Systems, Languages, and
|
|
Applications (OOPSLA), pp. 1-19, 1999. */
|
|
return true;
|
|
}
|
|
else if (TREE_CODE (stmt) == RETURN_EXPR)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Create a new memory tag of type TYPE. If IS_TYPE_TAG is true, the tag
|
|
is considered to represent all the pointers whose pointed-to types are
|
|
in the same alias set class. Otherwise, the tag represents a single
|
|
SSA_NAME pointer variable. */
|
|
|
|
static tree
|
|
create_memory_tag (tree type, bool is_type_tag)
|
|
{
|
|
var_ann_t ann;
|
|
tree tag = create_tmp_var_raw (type, (is_type_tag) ? "TMT" : "NMT");
|
|
|
|
/* By default, memory tags are local variables. Alias analysis will
|
|
determine whether they should be considered globals. */
|
|
DECL_CONTEXT (tag) = current_function_decl;
|
|
|
|
/* Memory tags are by definition addressable. This also prevents
|
|
is_gimple_ref frome confusing memory tags with optimizable
|
|
variables. */
|
|
TREE_ADDRESSABLE (tag) = 1;
|
|
|
|
ann = get_var_ann (tag);
|
|
ann->mem_tag_kind = (is_type_tag) ? TYPE_TAG : NAME_TAG;
|
|
ann->type_mem_tag = NULL_TREE;
|
|
|
|
/* Add the tag to the symbol table. */
|
|
add_referenced_tmp_var (tag);
|
|
|
|
return tag;
|
|
}
|
|
|
|
|
|
/* Create a name memory tag to represent a specific SSA_NAME pointer P_i.
|
|
This is used if P_i has been found to point to a specific set of
|
|
variables or to a non-aliased memory location like the address returned
|
|
by malloc functions. */
|
|
|
|
static tree
|
|
get_nmt_for (tree ptr)
|
|
{
|
|
struct ptr_info_def *pi = get_ptr_info (ptr);
|
|
tree tag = pi->name_mem_tag;
|
|
|
|
if (tag == NULL_TREE)
|
|
tag = create_memory_tag (TREE_TYPE (TREE_TYPE (ptr)), false);
|
|
|
|
/* If PTR is a PARM_DECL, it points to a global variable or malloc,
|
|
then its name tag should be considered a global variable. */
|
|
if (TREE_CODE (SSA_NAME_VAR (ptr)) == PARM_DECL
|
|
|| pi->pt_malloc
|
|
|| pi->pt_global_mem)
|
|
mark_call_clobbered (tag);
|
|
|
|
return tag;
|
|
}
|
|
|
|
|
|
/* Return the type memory tag associated to pointer PTR. A memory tag is an
|
|
artificial variable that represents the memory location pointed-to by
|
|
PTR. It is used to model the effects of pointer de-references on
|
|
addressable variables.
|
|
|
|
AI points to the data gathered during alias analysis. This function
|
|
populates the array AI->POINTERS. */
|
|
|
|
static tree
|
|
get_tmt_for (tree ptr, struct alias_info *ai)
|
|
{
|
|
size_t i;
|
|
tree tag;
|
|
tree tag_type = TREE_TYPE (TREE_TYPE (ptr));
|
|
HOST_WIDE_INT tag_set = get_alias_set (tag_type);
|
|
|
|
/* To avoid creating unnecessary memory tags, only create one memory tag
|
|
per alias set class. Note that it may be tempting to group
|
|
memory tags based on conflicting alias sets instead of
|
|
equivalence. That would be wrong because alias sets are not
|
|
necessarily transitive (as demonstrated by the libstdc++ test
|
|
23_containers/vector/cons/4.cc). Given three alias sets A, B, C
|
|
such that conflicts (A, B) == true and conflicts (A, C) == true,
|
|
it does not necessarily follow that conflicts (B, C) == true. */
|
|
for (i = 0, tag = NULL_TREE; i < ai->num_pointers; i++)
|
|
{
|
|
struct alias_map_d *curr = ai->pointers[i];
|
|
if (tag_set == curr->set)
|
|
{
|
|
tag = var_ann (curr->var)->type_mem_tag;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If VAR cannot alias with any of the existing memory tags, create a new
|
|
tag for PTR and add it to the POINTERS array. */
|
|
if (tag == NULL_TREE)
|
|
{
|
|
struct alias_map_d *alias_map;
|
|
|
|
/* If PTR did not have a type tag already, create a new TMT.*
|
|
artificial variable representing the memory location
|
|
pointed-to by PTR. */
|
|
if (var_ann (ptr)->type_mem_tag == NULL_TREE)
|
|
tag = create_memory_tag (tag_type, true);
|
|
else
|
|
tag = var_ann (ptr)->type_mem_tag;
|
|
|
|
/* Add PTR to the POINTERS array. Note that we are not interested in
|
|
PTR's alias set. Instead, we cache the alias set for the memory that
|
|
PTR points to. */
|
|
alias_map = xcalloc (1, sizeof (*alias_map));
|
|
alias_map->var = ptr;
|
|
alias_map->set = tag_set;
|
|
ai->pointers[ai->num_pointers++] = alias_map;
|
|
}
|
|
|
|
/* If the pointed-to type is volatile, so is the tag. */
|
|
TREE_THIS_VOLATILE (tag) |= TREE_THIS_VOLATILE (tag_type);
|
|
|
|
/* Make sure that the type tag has the same alias set as the
|
|
pointed-to type. */
|
|
gcc_assert (tag_set == get_alias_set (tag));
|
|
|
|
return tag;
|
|
}
|
|
|
|
|
|
/* Create GLOBAL_VAR, an artificial global variable to act as a
|
|
representative of all the variables that may be clobbered by function
|
|
calls. */
|
|
|
|
static void
|
|
create_global_var (void)
|
|
{
|
|
global_var = build_decl (VAR_DECL, get_identifier (".GLOBAL_VAR"),
|
|
void_type_node);
|
|
DECL_ARTIFICIAL (global_var) = 1;
|
|
TREE_READONLY (global_var) = 0;
|
|
DECL_EXTERNAL (global_var) = 1;
|
|
TREE_STATIC (global_var) = 1;
|
|
TREE_USED (global_var) = 1;
|
|
DECL_CONTEXT (global_var) = NULL_TREE;
|
|
TREE_THIS_VOLATILE (global_var) = 0;
|
|
TREE_ADDRESSABLE (global_var) = 0;
|
|
|
|
add_referenced_tmp_var (global_var);
|
|
bitmap_set_bit (vars_to_rename, var_ann (global_var)->uid);
|
|
}
|
|
|
|
|
|
/* Dump alias statistics on FILE. */
|
|
|
|
static void
|
|
dump_alias_stats (FILE *file)
|
|
{
|
|
const char *funcname
|
|
= lang_hooks.decl_printable_name (current_function_decl, 2);
|
|
fprintf (file, "\nAlias statistics for %s\n\n", funcname);
|
|
fprintf (file, "Total alias queries:\t%u\n", alias_stats.alias_queries);
|
|
fprintf (file, "Total alias mayalias results:\t%u\n",
|
|
alias_stats.alias_mayalias);
|
|
fprintf (file, "Total alias noalias results:\t%u\n",
|
|
alias_stats.alias_noalias);
|
|
fprintf (file, "Total simple queries:\t%u\n",
|
|
alias_stats.simple_queries);
|
|
fprintf (file, "Total simple resolved:\t%u\n",
|
|
alias_stats.simple_resolved);
|
|
fprintf (file, "Total TBAA queries:\t%u\n",
|
|
alias_stats.tbaa_queries);
|
|
fprintf (file, "Total TBAA resolved:\t%u\n",
|
|
alias_stats.tbaa_resolved);
|
|
}
|
|
|
|
|
|
/* Dump alias information on FILE. */
|
|
|
|
void
|
|
dump_alias_info (FILE *file)
|
|
{
|
|
size_t i;
|
|
const char *funcname
|
|
= lang_hooks.decl_printable_name (current_function_decl, 2);
|
|
|
|
fprintf (file, "\nFlow-insensitive alias information for %s\n\n", funcname);
|
|
|
|
fprintf (file, "Aliased symbols\n\n");
|
|
for (i = 0; i < num_referenced_vars; i++)
|
|
{
|
|
tree var = referenced_var (i);
|
|
if (may_be_aliased (var))
|
|
dump_variable (file, var);
|
|
}
|
|
|
|
fprintf (file, "\nDereferenced pointers\n\n");
|
|
for (i = 0; i < num_referenced_vars; i++)
|
|
{
|
|
tree var = referenced_var (i);
|
|
var_ann_t ann = var_ann (var);
|
|
if (ann->type_mem_tag)
|
|
dump_variable (file, var);
|
|
}
|
|
|
|
fprintf (file, "\nType memory tags\n\n");
|
|
for (i = 0; i < num_referenced_vars; i++)
|
|
{
|
|
tree var = referenced_var (i);
|
|
var_ann_t ann = var_ann (var);
|
|
if (ann->mem_tag_kind == TYPE_TAG)
|
|
dump_variable (file, var);
|
|
}
|
|
|
|
fprintf (file, "\n\nFlow-sensitive alias information for %s\n\n", funcname);
|
|
|
|
fprintf (file, "SSA_NAME pointers\n\n");
|
|
for (i = 1; i < num_ssa_names; i++)
|
|
{
|
|
tree ptr = ssa_name (i);
|
|
struct ptr_info_def *pi;
|
|
|
|
if (ptr == NULL_TREE)
|
|
continue;
|
|
|
|
pi = SSA_NAME_PTR_INFO (ptr);
|
|
if (!SSA_NAME_IN_FREE_LIST (ptr)
|
|
&& pi
|
|
&& pi->name_mem_tag)
|
|
dump_points_to_info_for (file, ptr);
|
|
}
|
|
|
|
fprintf (file, "\nName memory tags\n\n");
|
|
for (i = 0; i < num_referenced_vars; i++)
|
|
{
|
|
tree var = referenced_var (i);
|
|
var_ann_t ann = var_ann (var);
|
|
if (ann->mem_tag_kind == NAME_TAG)
|
|
dump_variable (file, var);
|
|
}
|
|
|
|
fprintf (file, "\n");
|
|
}
|
|
|
|
|
|
/* Dump alias information on stderr. */
|
|
|
|
void
|
|
debug_alias_info (void)
|
|
{
|
|
dump_alias_info (stderr);
|
|
}
|
|
|
|
|
|
/* Return the alias information associated with pointer T. It creates a
|
|
new instance if none existed. */
|
|
|
|
struct ptr_info_def *
|
|
get_ptr_info (tree t)
|
|
{
|
|
struct ptr_info_def *pi;
|
|
|
|
gcc_assert (POINTER_TYPE_P (TREE_TYPE (t)));
|
|
|
|
pi = SSA_NAME_PTR_INFO (t);
|
|
if (pi == NULL)
|
|
{
|
|
pi = ggc_alloc (sizeof (*pi));
|
|
memset ((void *)pi, 0, sizeof (*pi));
|
|
SSA_NAME_PTR_INFO (t) = pi;
|
|
}
|
|
|
|
return pi;
|
|
}
|
|
|
|
|
|
/* Dump points-to information for SSA_NAME PTR into FILE. */
|
|
|
|
void
|
|
dump_points_to_info_for (FILE *file, tree ptr)
|
|
{
|
|
struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
|
|
|
|
print_generic_expr (file, ptr, dump_flags);
|
|
|
|
if (pi)
|
|
{
|
|
if (pi->name_mem_tag)
|
|
{
|
|
fprintf (file, ", name memory tag: ");
|
|
print_generic_expr (file, pi->name_mem_tag, dump_flags);
|
|
}
|
|
|
|
if (pi->is_dereferenced)
|
|
fprintf (file, ", is dereferenced");
|
|
|
|
if (pi->value_escapes_p)
|
|
fprintf (file, ", its value escapes");
|
|
|
|
if (pi->pt_anything)
|
|
fprintf (file, ", points-to anything");
|
|
|
|
if (pi->pt_malloc)
|
|
fprintf (file, ", points-to malloc");
|
|
|
|
if (pi->pt_vars)
|
|
{
|
|
unsigned ix;
|
|
bitmap_iterator bi;
|
|
|
|
fprintf (file, ", points-to vars: { ");
|
|
EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, ix, bi)
|
|
{
|
|
print_generic_expr (file, referenced_var (ix), dump_flags);
|
|
fprintf (file, " ");
|
|
}
|
|
fprintf (file, "}");
|
|
}
|
|
}
|
|
|
|
fprintf (file, "\n");
|
|
}
|
|
|
|
|
|
/* Dump points-to information for VAR into stderr. */
|
|
|
|
void
|
|
debug_points_to_info_for (tree var)
|
|
{
|
|
dump_points_to_info_for (stderr, var);
|
|
}
|
|
|
|
|
|
/* Dump points-to information into FILE. NOTE: This function is slow, as
|
|
it needs to traverse the whole CFG looking for pointer SSA_NAMEs. */
|
|
|
|
void
|
|
dump_points_to_info (FILE *file)
|
|
{
|
|
basic_block bb;
|
|
block_stmt_iterator si;
|
|
size_t i;
|
|
ssa_op_iter iter;
|
|
const char *fname =
|
|
lang_hooks.decl_printable_name (current_function_decl, 2);
|
|
|
|
fprintf (file, "\n\nPointed-to sets for pointers in %s\n\n", fname);
|
|
|
|
/* First dump points-to information for the default definitions of
|
|
pointer variables. This is necessary because default definitions are
|
|
not part of the code. */
|
|
for (i = 0; i < num_referenced_vars; i++)
|
|
{
|
|
tree var = referenced_var (i);
|
|
if (POINTER_TYPE_P (TREE_TYPE (var)))
|
|
{
|
|
var_ann_t ann = var_ann (var);
|
|
if (ann->default_def)
|
|
dump_points_to_info_for (file, ann->default_def);
|
|
}
|
|
}
|
|
|
|
/* Dump points-to information for every pointer defined in the program. */
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
tree phi;
|
|
|
|
for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
|
|
{
|
|
tree ptr = PHI_RESULT (phi);
|
|
if (POINTER_TYPE_P (TREE_TYPE (ptr)))
|
|
dump_points_to_info_for (file, ptr);
|
|
}
|
|
|
|
for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
|
|
{
|
|
tree stmt = bsi_stmt (si);
|
|
tree def;
|
|
FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_DEF)
|
|
if (POINTER_TYPE_P (TREE_TYPE (def)))
|
|
dump_points_to_info_for (file, def);
|
|
}
|
|
}
|
|
|
|
fprintf (file, "\n");
|
|
}
|
|
|
|
|
|
/* Dump points-to info pointed by PTO into STDERR. */
|
|
|
|
void
|
|
debug_points_to_info (void)
|
|
{
|
|
dump_points_to_info (stderr);
|
|
}
|
|
|
|
/* Dump to FILE the list of variables that may be aliasing VAR. */
|
|
|
|
void
|
|
dump_may_aliases_for (FILE *file, tree var)
|
|
{
|
|
varray_type aliases;
|
|
|
|
if (TREE_CODE (var) == SSA_NAME)
|
|
var = SSA_NAME_VAR (var);
|
|
|
|
aliases = var_ann (var)->may_aliases;
|
|
if (aliases)
|
|
{
|
|
size_t i;
|
|
fprintf (file, "{ ");
|
|
for (i = 0; i < VARRAY_ACTIVE_SIZE (aliases); i++)
|
|
{
|
|
print_generic_expr (file, VARRAY_TREE (aliases, i), dump_flags);
|
|
fprintf (file, " ");
|
|
}
|
|
fprintf (file, "}");
|
|
}
|
|
}
|
|
|
|
|
|
/* Dump to stderr the list of variables that may be aliasing VAR. */
|
|
|
|
void
|
|
debug_may_aliases_for (tree var)
|
|
{
|
|
dump_may_aliases_for (stderr, var);
|
|
}
|
|
|
|
/* Return true if VAR may be aliased. */
|
|
|
|
bool
|
|
may_be_aliased (tree var)
|
|
{
|
|
/* Obviously. */
|
|
if (TREE_ADDRESSABLE (var))
|
|
return true;
|
|
|
|
/* Globally visible variables can have their addresses taken by other
|
|
translation units. */
|
|
if (DECL_EXTERNAL (var) || TREE_PUBLIC (var))
|
|
return true;
|
|
|
|
/* Automatic variables can't have their addresses escape any other way.
|
|
This must be after the check for global variables, as extern declarations
|
|
do not have TREE_STATIC set. */
|
|
if (!TREE_STATIC (var))
|
|
return false;
|
|
|
|
/* If we're in unit-at-a-time mode, then we must have seen all occurrences
|
|
of address-of operators, and so we can trust TREE_ADDRESSABLE. Otherwise
|
|
we can only be sure the variable isn't addressable if it's local to the
|
|
current function. */
|
|
if (flag_unit_at_a_time)
|
|
return false;
|
|
if (decl_function_context (var) == current_function_decl)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|